Repository: alexhiggins732/IdentityServer8 Branch: master Commit: 08051c9b2b7a Files: 1862 Total size: 34.8 MB Directory structure: gitextract__ksyciu0/ ├── .config/ │ └── dotnet-tools.json ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CONTRIBUTING.md │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── Question.md │ │ ├── bug_report.md │ │ └── feature_request.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── codeql.yml │ ├── develop.yml │ ├── master.yml │ ├── pre-release.yml │ └── release.yml ├── .gitignore ├── .readthedocs.yaml ├── Directory.Build.props ├── Directory.Build.targets ├── Directory.Packages.props ├── GitReleaseManager.yaml ├── IdentityServer8.DotNet.ruleset ├── LICENSE ├── LicenseHeader.txt ├── NuGet.config ├── README.md ├── SECURITY.MD ├── SPONSORS.md ├── docker-compose.dcproj ├── docker-compose.override.yml ├── docker-compose.vs.debug.yml ├── docker-compose.vs.release.yml ├── docker-compose.yml ├── docs/ │ ├── CHANGELOG.md │ ├── Makefile │ ├── autobuild.bat │ ├── build-documentation.ps1 │ ├── conf.py │ ├── docker-build.ps1 │ ├── dockerfile │ ├── endpoints/ │ │ ├── authorize.rst │ │ ├── device_authorization.rst │ │ ├── discovery.rst │ │ ├── endsession.rst │ │ ├── introspection.rst │ │ ├── revocation.rst │ │ ├── token.rst │ │ └── userinfo.rst │ ├── identityserver docs figures.pptx │ ├── index.rst │ ├── intro/ │ │ ├── big_picture.rst │ │ ├── contributing.rst │ │ ├── packaging.rst │ │ ├── specs.rst │ │ ├── support.rst │ │ ├── terminology.rst │ │ └── test.rst │ ├── make.bat │ ├── misc/ │ │ ├── blogs.rst │ │ ├── training.rst │ │ └── videos.rst │ ├── quickstarts/ │ │ ├── 0_overview.rst │ │ ├── 1_client_credentials.rst │ │ ├── 2_interactive_aspnetcore.rst │ │ ├── 3_aspnetcore_and_apis.rst │ │ ├── 4_javascript_client.rst │ │ ├── 5_entityframework.rst │ │ ├── 6_aspnet_identity.rst │ │ └── community.rst │ ├── readme.md │ ├── reference/ │ │ ├── api_resource.rst │ │ ├── api_scope.rst │ │ ├── aspnet_identity.rst │ │ ├── client.rst │ │ ├── deviceflow_interactionservice.rst │ │ ├── ef.rst │ │ ├── grant_validation_result.rst │ │ ├── identity_resource.rst │ │ ├── interactionservice.rst │ │ ├── options.rst │ │ └── profileservice.rst │ ├── requirements.txt │ └── topics/ │ ├── add_apis.rst │ ├── add_protocols.rst │ ├── apis.rst │ ├── client_authentication.rst │ ├── clients.rst │ ├── consent.rst │ ├── cors.rst │ ├── crypto.rst │ ├── custom_token_request_validation.rst │ ├── deployment.rst │ ├── discovery.rst │ ├── events.rst │ ├── extension_grants.rst │ ├── federation_gateway.rst │ ├── grant_types.rst │ ├── logging.rst │ ├── mtls.rst │ ├── persisted_grants.rst │ ├── pop.rst │ ├── reference_tokens.rst │ ├── refresh_tokens.rst │ ├── request_object.rst │ ├── resource_owner.rst │ ├── resources.rst │ ├── signin.rst │ ├── signin_external_providers.rst │ ├── signout.rst │ ├── signout_external_providers.rst │ ├── signout_federated.rst │ ├── startup.rst │ ├── tools.rst │ └── windows.rst ├── global.json ├── key.snk ├── main.cmd ├── samples/ │ ├── Clients/ │ │ ├── Clients.sln.licenseheader │ │ ├── ClientsGlobalUsings.cs │ │ ├── Directory.Build.props │ │ ├── old/ │ │ │ ├── Clients.sln │ │ │ ├── Clients.sln.licenseheader │ │ │ ├── Directory.Build.props │ │ │ ├── MvcHybrid/ │ │ │ │ ├── Controllers/ │ │ │ │ │ └── HomeController.cs │ │ │ │ ├── MvcHybrid.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Startup.cs │ │ │ │ ├── Views/ │ │ │ │ │ ├── Home/ │ │ │ │ │ │ ├── CallApi.cshtml │ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ │ └── Secure.cshtml │ │ │ │ │ ├── Shared/ │ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ │ └── _Layout.cshtml │ │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ │ └── _ViewStart.cshtml │ │ │ │ ├── appsettings.json │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ └── site.css │ │ │ │ └── js/ │ │ │ │ └── site.js │ │ │ ├── MvcHybridAutomaticRefresh/ │ │ │ │ ├── AutomaticTokenManagement/ │ │ │ │ │ ├── AutomaticTokenManagementBuilderExtensions.cs │ │ │ │ │ ├── AutomaticTokenManagementConfigureCookieOptions.cs │ │ │ │ │ ├── AutomaticTokenManagementCookieEvents.cs │ │ │ │ │ ├── AutomaticTokenManagementOptions.cs │ │ │ │ │ └── TokenEndpointService.cs │ │ │ │ ├── Controllers/ │ │ │ │ │ └── HomeController.cs │ │ │ │ ├── MvcHybridAutomaticRefresh.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Views/ │ │ │ │ │ ├── Home/ │ │ │ │ │ │ ├── CallApi.cshtml │ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ │ └── Secure.cshtml │ │ │ │ │ ├── Shared/ │ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ │ └── _Layout.cshtml │ │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ │ └── _ViewStart.cshtml │ │ │ │ ├── appsettings.json │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ └── site.css │ │ │ │ └── js/ │ │ │ │ └── site.js │ │ │ ├── MvcImplicit/ │ │ │ │ ├── Controllers/ │ │ │ │ │ └── HomeController.cs │ │ │ │ ├── MvcImplicit.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Startup.cs │ │ │ │ ├── Views/ │ │ │ │ │ ├── Home/ │ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ │ └── Secure.cshtml │ │ │ │ │ ├── Shared/ │ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ │ └── _Layout.cshtml │ │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ │ └── _ViewStart.cshtml │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ └── site.css │ │ │ │ └── js/ │ │ │ │ └── site.js │ │ │ ├── MvcImplicitJwtRequest/ │ │ │ │ ├── Controllers/ │ │ │ │ │ └── HomeController.cs │ │ │ │ ├── MvcImplicitJwtRequest.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Startup.cs │ │ │ │ ├── Views/ │ │ │ │ │ ├── Home/ │ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ │ └── Secure.cshtml │ │ │ │ │ ├── Shared/ │ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ │ └── _Layout.cshtml │ │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ │ └── _ViewStart.cshtml │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ └── site.css │ │ │ │ └── js/ │ │ │ │ └── site.js │ │ │ ├── MvcManual/ │ │ │ │ ├── Controllers/ │ │ │ │ │ └── HomeController.cs │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── MvcManual.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Startup.cs │ │ │ │ ├── Views/ │ │ │ │ │ ├── Home/ │ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ │ └── Secure.cshtml │ │ │ │ │ ├── Shared/ │ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ │ └── _Layout.cshtml │ │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ │ └── _ViewStart.cshtml │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ └── site.css │ │ │ │ └── js/ │ │ │ │ └── site.js │ │ │ └── MvcUsings.cs │ │ ├── readme.md │ │ ├── shared/ │ │ │ └── Constants/ │ │ │ ├── ConsoleExtensions.cs │ │ │ ├── Constants.cs │ │ │ ├── Constants.csproj │ │ │ └── TokenResponseExtensions.cs │ │ └── src/ │ │ ├── APIs/ │ │ │ ├── ResourceBasedApi/ │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ └── ResourceBasedApi.csproj │ │ │ └── SimpleApi/ │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ └── SimpleApi.csproj │ │ ├── Clients.sln │ │ ├── ConsoleClientCredentialsFlow/ │ │ │ ├── ConsoleClientCredentialsFlow.csproj │ │ │ └── Program.cs │ │ ├── ConsoleClientCredentialsFlowCallingIdentityServerApi/ │ │ │ ├── ConsoleClientCredentialsFlowCallingIdentityServerApi.csproj │ │ │ └── Program.cs │ │ ├── ConsoleClientCredentialsFlowPostBody/ │ │ │ ├── ConsoleClientCredentialsFlowPostBody.csproj │ │ │ └── Program.cs │ │ ├── ConsoleCode/ │ │ │ ├── ConsoleCode.csproj │ │ │ ├── Program.cs │ │ │ └── SystemBrowser.cs │ │ ├── ConsoleCustomGrant/ │ │ │ ├── ConsoleExtensionGrant.csproj │ │ │ └── Program.cs │ │ ├── ConsoleDeviceFlow/ │ │ │ ├── ConsoleDeviceFlow.csproj │ │ │ └── Program.cs │ │ ├── ConsoleEphemeralMtlsClient/ │ │ │ ├── ConsoleEphemeralMtlsClient.csproj │ │ │ ├── GlobalUsings.cs │ │ │ └── Program.cs │ │ ├── ConsoleIntrospectionClient/ │ │ │ ├── ConsoleIntrospectionClient.csproj │ │ │ └── Program.cs │ │ ├── ConsoleMTLSClient/ │ │ │ ├── ConsoleMTLSClient.csproj │ │ │ ├── Program.cs │ │ │ └── client.p12 │ │ ├── ConsoleParameterizedScopeClient/ │ │ │ ├── ConsoleParameterizedScopeClient.csproj │ │ │ └── Program.cs │ │ ├── ConsolePrivateKeyJwtClient/ │ │ │ ├── ConsolePrivateKeyJwtClient.csproj │ │ │ ├── Program.cs │ │ │ └── client.p12 │ │ ├── ConsoleResourceOwnerFlow/ │ │ │ ├── ConsoleResourceOwnerFlow.csproj │ │ │ └── Program.cs │ │ ├── ConsoleResourceOwnerFlowPublic/ │ │ │ ├── ConsoleResourceOwnerFlowPublic.csproj │ │ │ └── Program.cs │ │ ├── ConsoleResourceOwnerFlowReference/ │ │ │ ├── ConsoleResourceOwnerFlowReference.csproj │ │ │ └── Program.cs │ │ ├── ConsoleResourceOwnerFlowRefreshToken/ │ │ │ ├── ConsoleResourceOwnerFlowRefreshToken.csproj │ │ │ ├── GlobalUsings.cs │ │ │ └── Program.cs │ │ ├── ConsoleResourceOwnerFlowUserInfo/ │ │ │ ├── ConsoleResourceOwnerFlowUserInfo.csproj │ │ │ └── Program.cs │ │ ├── Directory.Build.props │ │ ├── JsOidc/ │ │ │ ├── JsOidc.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── web.config │ │ │ └── wwwroot/ │ │ │ ├── StyleSheet.css │ │ │ ├── app.js │ │ │ ├── callback.html │ │ │ ├── callback.js │ │ │ ├── index.html │ │ │ ├── libs/ │ │ │ │ ├── oidc-client.d.ts │ │ │ │ ├── oidc-client.js │ │ │ │ ├── oidc-client.rsa256.slim.js │ │ │ │ └── oidc-client.slim.js │ │ │ ├── popup.html │ │ │ ├── popup.js │ │ │ ├── silent.html │ │ │ └── silent.js │ │ ├── MvcAutomaticTokenManagement/ │ │ │ ├── Controllers/ │ │ │ │ └── HomeController.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── MvcAutomaticTokenManagement.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Views/ │ │ │ │ ├── Home/ │ │ │ │ │ ├── CallApi.cshtml │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Secure.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ └── _ValidationScriptsPartial.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ └── site.css │ │ │ └── js/ │ │ │ └── site.js │ │ ├── MvcCode/ │ │ │ ├── Controllers/ │ │ │ │ └── HomeController.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── MvcCode.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Views/ │ │ │ │ ├── Home/ │ │ │ │ │ ├── CallApi.cshtml │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Secure.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ └── _ValidationScriptsPartial.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ └── site.css │ │ │ └── js/ │ │ │ └── site.js │ │ ├── MvcHybridBackChannel/ │ │ │ ├── Controllers/ │ │ │ │ ├── HomeController.cs │ │ │ │ └── LogoutController.cs │ │ │ ├── CookieEventHandler.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── LogoutSessionManager.cs │ │ │ ├── MvcHybridBackChannel.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Views/ │ │ │ │ ├── Home/ │ │ │ │ │ ├── CallApi.cshtml │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Secure.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ └── _Layout.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── _references.js │ │ │ ├── css/ │ │ │ │ └── site.css │ │ │ └── js/ │ │ │ └── site.js │ │ └── WindowsConsoleSystemBrowser/ │ │ ├── CallbackManager.cs │ │ ├── Program.cs │ │ ├── RegistryConfig.cs │ │ └── WindowsConsoleSystemBrowser.csproj │ ├── Directory.Build.props │ ├── KeyManagement/ │ │ ├── Directory.Build.props │ │ ├── FileSystemKeys/ │ │ │ ├── Directory.Build.props │ │ │ ├── FileSystem/ │ │ │ │ ├── Config.cs │ │ │ │ ├── FileSystemSample.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Startup.cs │ │ │ │ └── appsettings.json │ │ │ ├── KeyManagement.sln │ │ │ ├── KeyManagement.sln.licenseheader │ │ │ └── database/ │ │ │ ├── EF/ │ │ │ │ ├── Config.cs │ │ │ │ ├── EfSample.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Startup.cs │ │ │ │ └── appsettings.json │ │ │ └── migrations/ │ │ │ ├── Migrations/ │ │ │ │ ├── KeyManagement/ │ │ │ │ │ ├── 20200327143521_KeyManagement.Designer.cs │ │ │ │ │ ├── 20200327143521_KeyManagement.cs │ │ │ │ │ └── KeyManagementDbContextModelSnapshot.cs │ │ │ │ └── KeyManagement.sql │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Startup.cs │ │ │ ├── appsettings.json │ │ │ ├── builddb.bat │ │ │ └── migrations.csproj │ │ └── KeyManagementGlobalUsings.cs │ ├── Quickstarts/ │ │ ├── 1_ClientCredentials/ │ │ │ ├── Directory.Build.props │ │ │ ├── Quickstart.sln │ │ │ ├── Quickstart.sln.licenseheader │ │ │ └── src/ │ │ │ └── IdentityServer/ │ │ │ ├── GlobalUsings.cs │ │ │ ├── IdentityServer.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ └── libman.json │ │ ├── 2_InteractiveAspNetCore/ │ │ │ ├── Quickstart.sln │ │ │ ├── Quickstart.sln.licenseheader │ │ │ └── src/ │ │ │ ├── IdentityServer/ │ │ │ │ ├── Config.cs │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── IdentityServer.csproj │ │ │ │ ├── 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 │ │ │ │ ├── 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 │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ ├── site.css │ │ │ │ │ └── site.scss │ │ │ │ └── js/ │ │ │ │ ├── signin-redirect.js │ │ │ │ └── signout-redirect.js │ │ │ └── MvcClient/ │ │ │ ├── Controllers/ │ │ │ │ └── HomeController.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Models/ │ │ │ │ └── ErrorViewModel.cs │ │ │ ├── MvcClient.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Views/ │ │ │ │ ├── Home/ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Privacy.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ │ └── json.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ └── site.css │ │ │ └── js/ │ │ │ └── site.js │ │ ├── 3_AspNetCoreAndApis/ │ │ │ ├── Quickstart.sln │ │ │ ├── Quickstart.sln.licenseheader │ │ │ └── src/ │ │ │ ├── IdentityServer/ │ │ │ │ ├── Config.cs │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── IdentityServer.csproj │ │ │ │ ├── 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 │ │ │ │ ├── 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 │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ ├── site.css │ │ │ │ │ └── site.scss │ │ │ │ └── js/ │ │ │ │ ├── signin-redirect.js │ │ │ │ └── signout-redirect.js │ │ │ └── MvcClient/ │ │ │ ├── Controllers/ │ │ │ │ └── HomeController.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Models/ │ │ │ │ └── ErrorViewModel.cs │ │ │ ├── MvcClient.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Views/ │ │ │ │ ├── Home/ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Privacy.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ │ └── json.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ └── site.css │ │ │ └── js/ │ │ │ └── site.js │ │ ├── 4_JavaScriptClient/ │ │ │ ├── Quickstart.sln │ │ │ ├── Quickstart.sln.licenseheader │ │ │ └── src/ │ │ │ ├── IdentityServer/ │ │ │ │ ├── Config.cs │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── IdentityServer.csproj │ │ │ │ ├── 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 │ │ │ │ ├── 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 │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ ├── site.css │ │ │ │ │ └── site.scss │ │ │ │ └── js/ │ │ │ │ ├── signin-redirect.js │ │ │ │ └── signout-redirect.js │ │ │ ├── JavaScriptClient/ │ │ │ │ ├── JavaScriptClient.csproj │ │ │ │ ├── Program.cs │ │ │ │ ├── Properties/ │ │ │ │ │ └── launchSettings.json │ │ │ │ ├── Startup.cs │ │ │ │ ├── appsettings.Development.json │ │ │ │ ├── appsettings.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── app.js │ │ │ │ ├── callback.html │ │ │ │ ├── index.html │ │ │ │ └── oidc-client.js │ │ │ └── MvcClient/ │ │ │ ├── Controllers/ │ │ │ │ └── HomeController.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Models/ │ │ │ │ └── ErrorViewModel.cs │ │ │ ├── MvcClient.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Startup.cs │ │ │ ├── Views/ │ │ │ │ ├── Home/ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Privacy.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ │ └── json.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ └── site.css │ │ │ └── js/ │ │ │ └── site.js │ │ ├── 5_EntityFramework/ │ │ │ ├── Quickstart.sln │ │ │ ├── Quickstart.sln.licenseheader │ │ │ └── src/ │ │ │ ├── IdentityServer/ │ │ │ │ ├── Config.cs │ │ │ │ ├── Data/ │ │ │ │ │ └── Migrations/ │ │ │ │ │ └── IdentityServer/ │ │ │ │ │ ├── ConfigurationDb/ │ │ │ │ │ │ ├── 20200625203625_InitialIdentityServerConfigurationDbMigration.Designer.cs │ │ │ │ │ │ ├── 20200625203625_InitialIdentityServerConfigurationDbMigration.cs │ │ │ │ │ │ └── ConfigurationDbContextModelSnapshot.cs │ │ │ │ │ └── PersistedGrantDb/ │ │ │ │ │ ├── 20200625203357_InitialIdentityServerPersistedGrantDbMigration.Designer.cs │ │ │ │ │ ├── 20200625203357_InitialIdentityServerPersistedGrantDbMigration.cs │ │ │ │ │ └── PersistedGrantDbContextModelSnapshot.cs │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── IdentityServer.csproj │ │ │ │ ├── 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 │ │ │ │ ├── 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 │ │ │ │ ├── libman.json │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ ├── site.css │ │ │ │ │ └── site.scss │ │ │ │ └── js/ │ │ │ │ ├── signin-redirect.js │ │ │ │ └── signout-redirect.js │ │ │ └── MvcClient/ │ │ │ ├── Controllers/ │ │ │ │ └── HomeController.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Models/ │ │ │ │ └── ErrorViewModel.cs │ │ │ ├── MvcClient.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Views/ │ │ │ │ ├── Home/ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Privacy.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ │ └── json.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ └── site.css │ │ │ └── js/ │ │ │ └── site.js │ │ ├── 6_AspNetIdentity/ │ │ │ ├── Quickstart.sln │ │ │ ├── Quickstart.sln.licenseheader │ │ │ └── src/ │ │ │ ├── IdentityServerAspNetIdentity/ │ │ │ │ ├── Config.cs │ │ │ │ ├── Data/ │ │ │ │ │ ├── ApplicationDbContext.cs │ │ │ │ │ └── Migrations/ │ │ │ │ │ ├── 20180109192453_CreateIdentitySchema.Designer.cs │ │ │ │ │ ├── 20180109192453_CreateIdentitySchema.cs │ │ │ │ │ └── ApplicationDbContextModelSnapshot.cs │ │ │ │ ├── GlobalUsings.cs │ │ │ │ ├── IdentityServerAspNetIdentity.csproj │ │ │ │ ├── Models/ │ │ │ │ │ └── ApplicationUser.cs │ │ │ │ ├── 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 │ │ │ │ ├── SeedData.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 │ │ │ │ ├── libman.json │ │ │ │ ├── updateUI.ps1 │ │ │ │ └── wwwroot/ │ │ │ │ ├── css/ │ │ │ │ │ ├── site.css │ │ │ │ │ └── site.scss │ │ │ │ └── js/ │ │ │ │ ├── signin-redirect.js │ │ │ │ └── signout-redirect.js │ │ │ └── MvcClient/ │ │ │ ├── Controllers/ │ │ │ │ └── HomeController.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Models/ │ │ │ │ └── ErrorViewModel.cs │ │ │ ├── MvcClient.csproj │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── Views/ │ │ │ │ ├── Home/ │ │ │ │ │ ├── Index.cshtml │ │ │ │ │ └── Privacy.cshtml │ │ │ │ ├── Shared/ │ │ │ │ │ ├── Error.cshtml │ │ │ │ │ ├── _Layout.cshtml │ │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ │ └── json.cshtml │ │ │ │ ├── _ViewImports.cshtml │ │ │ │ └── _ViewStart.cshtml │ │ │ ├── appsettings.Development.json │ │ │ ├── appsettings.json │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ └── site.css │ │ │ └── js/ │ │ │ └── site.js │ │ ├── Directory.Build.props │ │ ├── Quickstart.sln.licenseheader │ │ └── Shared/ │ │ └── src/ │ │ ├── Api/ │ │ │ ├── Api.csproj │ │ │ ├── Program.cs │ │ │ └── Properties/ │ │ │ └── launchSettings.json │ │ ├── Client/ │ │ │ ├── Client.csproj │ │ │ └── Program.cs │ │ ├── IdentityServer/ │ │ │ ├── Config.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── IdentityServer.csproj │ │ │ ├── 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 │ │ │ ├── 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 │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ ├── site.css │ │ │ │ └── site.scss │ │ │ └── js/ │ │ │ ├── signin-redirect.js │ │ │ └── signout-redirect.js │ │ └── MvcClient/ │ │ ├── Controllers/ │ │ │ └── HomeController.cs │ │ ├── GlobalUsings.cs │ │ ├── Models/ │ │ │ └── ErrorViewModel.cs │ │ ├── MvcClient.csproj │ │ ├── Program.cs │ │ ├── Properties/ │ │ │ └── launchSettings.json │ │ ├── Views/ │ │ │ ├── Home/ │ │ │ │ ├── Index.cshtml │ │ │ │ └── Privacy.cshtml │ │ │ ├── Shared/ │ │ │ │ ├── Error.cshtml │ │ │ │ ├── _Layout.cshtml │ │ │ │ ├── _ValidationScriptsPartial.cshtml │ │ │ │ └── json.cshtml │ │ │ ├── _ViewImports.cshtml │ │ │ └── _ViewStart.cshtml │ │ ├── appsettings.Development.json │ │ ├── appsettings.json │ │ ├── libman.json │ │ └── wwwroot/ │ │ ├── css/ │ │ │ └── site.css │ │ └── js/ │ │ └── site.js │ └── SamplesGlobalUsings.cs ├── src/ │ ├── AspNetIdentity/ │ │ ├── Directory.Build.props │ │ ├── IdentityServer8.AspNetIdentity.sln │ │ ├── README.md │ │ ├── build.cmd │ │ ├── build.ps1 │ │ ├── build.sh │ │ ├── host/ │ │ │ ├── Configuration/ │ │ │ │ ├── Clients.cs │ │ │ │ ├── ClientsConsole.cs │ │ │ │ ├── ClientsWeb.cs │ │ │ │ └── Resources.cs │ │ │ ├── Data/ │ │ │ │ └── ApplicationDbContext.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Host.csproj │ │ │ ├── Models/ │ │ │ │ ├── AccountViewModels/ │ │ │ │ │ ├── ExternalLoginViewModel.cs │ │ │ │ │ ├── ForgotPasswordViewModel.cs │ │ │ │ │ ├── LoginViewModel.cs │ │ │ │ │ ├── LoginWith2faViewModel.cs │ │ │ │ │ ├── LoginWithRecoveryCodeViewModel.cs │ │ │ │ │ ├── RegisterViewModel.cs │ │ │ │ │ └── ResetPasswordViewModel.cs │ │ │ │ ├── ApplicationUser.cs │ │ │ │ └── ManageViewModels/ │ │ │ │ ├── ChangePasswordViewModel.cs │ │ │ │ ├── EnableAuthenticatorViewModel.cs │ │ │ │ ├── ExternalLoginsViewModel.cs │ │ │ │ ├── GenerateRecoveryCodesViewModel.cs │ │ │ │ ├── IndexViewModel.cs │ │ │ │ ├── RemoveLoginViewModel.cs │ │ │ │ ├── SetPasswordViewModel.cs │ │ │ │ └── TwoFactorAuthenticationViewModel.cs │ │ │ ├── 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 │ │ │ ├── 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 │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ ├── site.css │ │ │ │ └── site.scss │ │ │ └── js/ │ │ │ ├── signin-redirect.js │ │ │ └── signout-redirect.js │ │ ├── migrations/ │ │ │ └── SqlServer/ │ │ │ ├── GlobalUsings.cs │ │ │ ├── Migrations/ │ │ │ │ ├── UsersDb/ │ │ │ │ │ ├── 20200323135751_Users.Designer.cs │ │ │ │ │ ├── 20200323135751_Users.cs │ │ │ │ │ └── ApplicationDbContextModelSnapshot.cs │ │ │ │ └── UsersDb.sql │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── SeedData.cs │ │ │ ├── SqlServer.csproj │ │ │ ├── Startup.cs │ │ │ ├── appsettings.json │ │ │ └── builddb.bat │ │ └── src/ │ │ ├── Decorator.cs │ │ ├── GlobalUsings.cs │ │ ├── IdentityServer8.AspNetIdentity.csproj │ │ ├── IdentityServerBuilderExtensions.cs │ │ ├── ProfileService.cs │ │ ├── ResourceOwnerPasswordValidator.cs │ │ ├── SecurityStampValidatorCallback.cs │ │ └── UserClaimsFactory.cs │ ├── Directory.Build.props │ ├── Directory.BuildOld.targets │ ├── EntityFramework/ │ │ ├── Directory.Build.props │ │ ├── IdentityServer8.EntityFramework.sln │ │ ├── README.md │ │ ├── build.cmd │ │ ├── build.ps1 │ │ ├── build.sh │ │ ├── dropdb.bat │ │ ├── host/ │ │ │ ├── GlobalUsings.cs │ │ │ ├── Host.csproj │ │ │ ├── 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 │ │ │ ├── Startup.cs │ │ │ ├── TestOperationalStoreNotification.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 │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ ├── site.css │ │ │ │ └── site.scss │ │ │ └── js/ │ │ │ ├── signin-redirect.js │ │ │ └── signout-redirect.js │ │ ├── migrations/ │ │ │ └── SqlServer/ │ │ │ ├── Configuration/ │ │ │ │ ├── Clients.cs │ │ │ │ ├── ClientsConsole.cs │ │ │ │ ├── ClientsWeb.cs │ │ │ │ └── Resources.cs │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── SeedData.cs │ │ │ ├── SqlServer.csproj │ │ │ ├── Startup.cs │ │ │ └── appsettings.json │ │ ├── migrations.bat │ │ ├── src/ │ │ │ ├── GlobalUsings.cs │ │ │ ├── IdentityServer8.EntityFramework.csproj │ │ │ ├── IdentityServerEntityFrameworkBuilderExtensions.cs │ │ │ ├── Services/ │ │ │ │ └── CorsPolicyService.cs │ │ │ └── TokenCleanupHost.cs │ │ ├── test/ │ │ │ └── IdentityServer8.EntityFramework.Tests/ │ │ │ ├── DatabaseProviderBuilder.cs │ │ │ ├── DatabaseProviderFixture.cs │ │ │ ├── FakeLogger.cs │ │ │ ├── IdentityServer8.EntityFramework.Tests.csproj │ │ │ ├── IntegrationTest.cs │ │ │ └── Services/ │ │ │ └── CorsPolicyServiceTests.cs │ │ └── updatedb.bat │ ├── EntityFramework.Storage/ │ │ ├── Directory.Build.props │ │ ├── IdentityServer8.EntityFramework.Storage.sln │ │ ├── README.md │ │ ├── build.cmd │ │ ├── build.ps1 │ │ ├── build.sh │ │ ├── host/ │ │ │ ├── ConsoleHost/ │ │ │ │ ├── ConsoleHost.csproj │ │ │ │ ├── GlobalUsings.cs │ │ │ │ └── Program.cs │ │ │ └── Directory.Build.props │ │ ├── migrations/ │ │ │ ├── Directory.Build.props │ │ │ └── SqlServer/ │ │ │ ├── GlobalUsings.cs │ │ │ ├── Migrations/ │ │ │ │ ├── ConfigurationDb/ │ │ │ │ │ ├── 20200522172542_Config.Designer.cs │ │ │ │ │ ├── 20200522172542_Config.cs │ │ │ │ │ └── ConfigurationDbContextModelSnapshot.cs │ │ │ │ ├── ConfigurationDb.sql │ │ │ │ ├── PersistedGrantDb/ │ │ │ │ │ ├── 20200522172538_Grants.Designer.cs │ │ │ │ │ ├── 20200522172538_Grants.cs │ │ │ │ │ └── PersistedGrantDbContextModelSnapshot.cs │ │ │ │ └── PersistedGrantDb.sql │ │ │ ├── Program.cs │ │ │ ├── Properties/ │ │ │ │ └── launchSettings.json │ │ │ ├── SqlServer.csproj │ │ │ ├── Startup.cs │ │ │ ├── appsettings.json │ │ │ ├── buildschema.bat │ │ │ └── createdb.bat │ │ ├── src/ │ │ │ ├── Configuration/ │ │ │ │ └── ServiceCollectionExtensions.cs │ │ │ ├── DbContexts/ │ │ │ │ ├── ConfigurationDbContext.cs │ │ │ │ └── PersistedGrantDbContext.cs │ │ │ ├── Entities/ │ │ │ │ ├── ApiResource.cs │ │ │ │ ├── ApiResourceClaim.cs │ │ │ │ ├── ApiResourceProperty.cs │ │ │ │ ├── ApiResourceScope.cs │ │ │ │ ├── ApiResourceSecret.cs │ │ │ │ ├── ApiScope.cs │ │ │ │ ├── ApiScopeClaim.cs │ │ │ │ ├── ApiScopeProperty.cs │ │ │ │ ├── Client.cs │ │ │ │ ├── ClientClaim.cs │ │ │ │ ├── ClientCorsOrigin.cs │ │ │ │ ├── ClientGrantType.cs │ │ │ │ ├── ClientIdPRestriction.cs │ │ │ │ ├── ClientPostLogoutRedirectUri.cs │ │ │ │ ├── ClientProperty.cs │ │ │ │ ├── ClientRedirectUri.cs │ │ │ │ ├── ClientScope.cs │ │ │ │ ├── ClientSecret.cs │ │ │ │ ├── DeviceFlowCodes.cs │ │ │ │ ├── IdentityResource.cs │ │ │ │ ├── IdentityResourceClaim.cs │ │ │ │ ├── IdentityResourceProperty.cs │ │ │ │ ├── PersistedGrant.cs │ │ │ │ ├── Property.cs │ │ │ │ ├── Secret.cs │ │ │ │ └── UserClaim.cs │ │ │ ├── Extensions/ │ │ │ │ └── ModelBuilderExtensions.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── IdentityServer8.EntityFramework.Storage.csproj │ │ │ ├── Interfaces/ │ │ │ │ ├── IConfigurationDbContext.cs │ │ │ │ └── IPersistedGrantDbContext.cs │ │ │ ├── Mappers/ │ │ │ │ ├── AllowedSigningAlgorithmsConverter.cs │ │ │ │ ├── ApiResourceMapperProfile.cs │ │ │ │ ├── ApiResourceMappers.cs │ │ │ │ ├── ClientMapperProfile.cs │ │ │ │ ├── ClientMappers.cs │ │ │ │ ├── IdentityResourceMapperProfile.cs │ │ │ │ ├── IdentityResourceMappers.cs │ │ │ │ ├── PersistedGrantMapperProfile.cs │ │ │ │ ├── PersistedGrantMappers.cs │ │ │ │ ├── ScopeMapperProfile.cs │ │ │ │ └── ScopeMappers.cs │ │ │ ├── Options/ │ │ │ │ ├── ConfigurationStoreOptions.cs │ │ │ │ ├── OperationalStoreOptions.cs │ │ │ │ └── TableConfiguration.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── Stores/ │ │ │ │ ├── ClientStore.cs │ │ │ │ ├── DeviceFlowStore.cs │ │ │ │ ├── PersistedGrantStore.cs │ │ │ │ └── ResourceStore.cs │ │ │ └── TokenCleanup/ │ │ │ ├── IOperationalStoreNotification.cs │ │ │ └── TokenCleanupService.cs │ │ └── test/ │ │ ├── Directory.Build.props │ │ ├── IntegrationTests/ │ │ │ ├── DatabaseProviderBuilder.cs │ │ │ ├── DatabaseProviderFixture.cs │ │ │ ├── DbContexts/ │ │ │ │ └── ClientDbContextTests.cs │ │ │ ├── FakeLogger.cs │ │ │ ├── IdentityServer8.EntityFramework.IntegrationTests.csproj │ │ │ ├── IntegrationTest.cs │ │ │ ├── Stores/ │ │ │ │ ├── ClientStoreTests.cs │ │ │ │ ├── DeviceFlowStoreTests.cs │ │ │ │ ├── PersistedGrantStoreTests.cs │ │ │ │ └── ResourceStoreTests.cs │ │ │ └── TokenCleanup/ │ │ │ └── TokenCleanupTests.cs │ │ └── UnitTests/ │ │ ├── IdentityServer8.EntityFramework.UnitTests.csproj │ │ └── Mappers/ │ │ ├── ApiResourceMappersTests.cs │ │ ├── ClientMappersTests.cs │ │ ├── IdentityResourcesMappersTests.cs │ │ ├── PersistedGrantMappersTests.cs │ │ └── ScopeMappersTests.cs │ ├── IdentityServer8/ │ │ ├── Directory.Build.props │ │ ├── IdentityServer8.sln │ │ ├── build.cmd │ │ ├── build.ps1 │ │ ├── build.sh │ │ ├── host/ │ │ │ ├── Configuration/ │ │ │ │ ├── Clients.cs │ │ │ │ ├── ClientsConsole.cs │ │ │ │ ├── ClientsWeb.cs │ │ │ │ └── Resources.cs │ │ │ ├── Extensions/ │ │ │ │ ├── ExtensionGrantValidator.cs │ │ │ │ ├── HostProfileService.cs │ │ │ │ ├── NoSubjectExtensionGrantValidator.cs │ │ │ │ ├── ParameterizedScopeParser.cs │ │ │ │ ├── ParameterizedScopeTokenRequestValidator.cs │ │ │ │ └── SameSiteHandlingExtensions.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Host.csproj │ │ │ ├── Keys/ │ │ │ │ ├── identityserver.test.ecdsa.p12 │ │ │ │ └── identityserver.test.rsa.p12 │ │ │ ├── LocalApiController.cs │ │ │ ├── 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 │ │ │ ├── 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 │ │ │ ├── compilerconfig.json │ │ │ ├── compilerconfig.json.defaults │ │ │ ├── libman.json │ │ │ └── wwwroot/ │ │ │ ├── css/ │ │ │ │ ├── site.css │ │ │ │ └── site.scss │ │ │ └── js/ │ │ │ ├── signin-redirect.js │ │ │ └── signout-redirect.js │ │ ├── src/ │ │ │ ├── Configuration/ │ │ │ │ ├── CryptoHelper.cs │ │ │ │ ├── DependencyInjection/ │ │ │ │ │ ├── BuilderExtensions/ │ │ │ │ │ │ ├── Additional.cs │ │ │ │ │ │ ├── Core.cs │ │ │ │ │ │ ├── Crypto.cs │ │ │ │ │ │ └── InMemory.cs │ │ │ │ │ ├── ConfigureInternalCookieOptions.cs │ │ │ │ │ ├── ConfigureOpenIdConnectOptions.cs │ │ │ │ │ ├── Decorator.cs │ │ │ │ │ ├── IIdentityServerBuilder.cs │ │ │ │ │ ├── IdentityServerBuilder.cs │ │ │ │ │ ├── IdentityServerServiceCollectionExtensions.cs │ │ │ │ │ └── Options/ │ │ │ │ │ ├── AuthenticationOptions.cs │ │ │ │ │ ├── CachingOptions.cs │ │ │ │ │ ├── CorsOptions.cs │ │ │ │ │ ├── CspOptions.cs │ │ │ │ │ ├── DeviceFlowOptions.cs │ │ │ │ │ ├── DiscoveryOptions.cs │ │ │ │ │ ├── EndpointOptions.cs │ │ │ │ │ ├── EventsOptions.cs │ │ │ │ │ ├── IdentityServerOptions.cs │ │ │ │ │ ├── InputLengthRestrictions.cs │ │ │ │ │ ├── LoggingOptions.cs │ │ │ │ │ ├── MtlsOptions.cs │ │ │ │ │ ├── UserInteractionOptions.cs │ │ │ │ │ └── ValidationOptions.cs │ │ │ │ ├── IdentityServerApplicationBuilderExtensions.cs │ │ │ │ └── IdentityServerMiddlewareOptions.cs │ │ │ ├── Constants.cs │ │ │ ├── Endpoints/ │ │ │ │ ├── AuthorizeCallbackEndpoint.cs │ │ │ │ ├── AuthorizeEndpoint.cs │ │ │ │ ├── AuthorizeEndpointBase.cs │ │ │ │ ├── CheckSessionEndpoint.cs │ │ │ │ ├── DeviceAuthorizationEndpoint.cs │ │ │ │ ├── DiscoveryEndpoint.cs │ │ │ │ ├── DiscoveryKeyEndpoint.cs │ │ │ │ ├── EndSessionCallbackEndpoint.cs │ │ │ │ ├── EndSessionEndpoint.cs │ │ │ │ ├── IntrospectionEndpoint.cs │ │ │ │ ├── Results/ │ │ │ │ │ ├── AuthorizeResult.cs │ │ │ │ │ ├── BadRequestResult.cs │ │ │ │ │ ├── CheckSessionResult.cs │ │ │ │ │ ├── ConsentPageResult.cs │ │ │ │ │ ├── CustomRedirectResult.cs │ │ │ │ │ ├── DeviceAuthorizationResult.cs │ │ │ │ │ ├── DiscoveryDocumentResult.cs │ │ │ │ │ ├── EndSessionCallbackResult.cs │ │ │ │ │ ├── EndSessionResult.cs │ │ │ │ │ ├── IntrospectionResult.cs │ │ │ │ │ ├── JsonWebKeysResult.cs │ │ │ │ │ ├── LoginPageResult.cs │ │ │ │ │ ├── ProtectedResourceErrorResult.cs │ │ │ │ │ ├── StatusCodeResult.cs │ │ │ │ │ ├── TokenErrorResult.cs │ │ │ │ │ ├── TokenResult.cs │ │ │ │ │ ├── TokenRevocationErrorResult.cs │ │ │ │ │ └── UserInfoResult.cs │ │ │ │ ├── TokenEndpoint.cs │ │ │ │ ├── TokenRevocationEndpoint.cs │ │ │ │ └── UserInfoEndpoint.cs │ │ │ ├── Events/ │ │ │ │ ├── ApiAuthenticationFailureEvent.cs │ │ │ │ ├── ApiAuthenticationSuccessEvent.cs │ │ │ │ ├── ClientAuthenticationFailureEvent.cs │ │ │ │ ├── ClientAuthenticationSuccessEvent.cs │ │ │ │ ├── ConsentDeniedEvent.cs │ │ │ │ ├── ConsentGrantedEvent.cs │ │ │ │ ├── DeviceAuthorizationFailureEvent.cs │ │ │ │ ├── DeviceAuthorizationSuccessEvent.cs │ │ │ │ ├── GrantsRevokedEvent.cs │ │ │ │ ├── Infrastructure/ │ │ │ │ │ ├── Event.cs │ │ │ │ │ ├── EventCategories.cs │ │ │ │ │ ├── EventIds.cs │ │ │ │ │ └── EventType.cs │ │ │ │ ├── InvalidClientConfiguration.cs │ │ │ │ ├── TokenIntrospectionFailureEvent.cs │ │ │ │ ├── TokenIntrospectionSuccessEvent.cs │ │ │ │ ├── TokenIssuedFailureEvent.cs │ │ │ │ ├── TokenIssuedSuccessEvent.cs │ │ │ │ ├── TokenRevokedSuccessEvent.cs │ │ │ │ ├── UnhandledExceptionEvent.cs │ │ │ │ ├── UserLoginFailureEvent.cs │ │ │ │ ├── UserLoginSuccessEvent.cs │ │ │ │ └── UserLogoutSuccessEvent.cs │ │ │ ├── Extensions/ │ │ │ │ ├── AuthenticationPropertiesExtensions.cs │ │ │ │ ├── AuthorizeResponseExtensions.cs │ │ │ │ ├── ClaimsExtensions.cs │ │ │ │ ├── ClientExtensions.cs │ │ │ │ ├── DateTimeExtensions.cs │ │ │ │ ├── EndpointOptionsExtensions.cs │ │ │ │ ├── HashExtensions.cs │ │ │ │ ├── HttpContextAuthenticationExtensions.cs │ │ │ │ ├── HttpContextExtensions.cs │ │ │ │ ├── HttpRequestExtensions.cs │ │ │ │ ├── HttpResponseExtensions.cs │ │ │ │ ├── ICacheExtensions.cs │ │ │ │ ├── IClientStoreExtensions.cs │ │ │ │ ├── IEnumerableExtensions.cs │ │ │ │ ├── IReadableStringCollectionExtensions.cs │ │ │ │ ├── IResourceStoreExtensions.cs │ │ │ │ ├── IUserSessionExtensions.cs │ │ │ │ ├── IdentityServerToolsExtensions.cs │ │ │ │ ├── JsonExtensions.cs │ │ │ │ ├── NameValueCollectionExtensions.cs │ │ │ │ ├── PrincipalExtensions.cs │ │ │ │ ├── ProfileDataRequestContextExtensions.cs │ │ │ │ ├── ResourceExtensions.cs │ │ │ │ ├── ScopeExtensions.cs │ │ │ │ ├── StringsExtensions.cs │ │ │ │ ├── TokenExtensions.cs │ │ │ │ ├── ValidatedAuthorizeRequestExtensions.cs │ │ │ │ └── X509CertificateExtensions.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── Hosting/ │ │ │ │ ├── BaseUrlMiddleware.cs │ │ │ │ ├── CorsMiddleware.cs │ │ │ │ ├── CorsPolicyProvider.cs │ │ │ │ ├── Endpoint.cs │ │ │ │ ├── EndpointRouter.cs │ │ │ │ ├── FederatedSignOut/ │ │ │ │ │ ├── AuthenticationRequestHandlerWrapper.cs │ │ │ │ │ ├── AuthenticationRequestSignInHandlerWrapper.cs │ │ │ │ │ ├── AuthenticationRequestSignOutHandlerWrapper.cs │ │ │ │ │ └── FederatedSignoutAuthenticationHandlerProvider.cs │ │ │ │ ├── IEndpointHandler.cs │ │ │ │ ├── IEndpointResult.cs │ │ │ │ ├── IEndpointRouter.cs │ │ │ │ ├── IdentityServerAuthenticationService.cs │ │ │ │ ├── IdentityServerMiddleware.cs │ │ │ │ ├── LocalApiAuthentication/ │ │ │ │ │ ├── LocalApiAuthenticationEvents.cs │ │ │ │ │ ├── LocalApiAuthenticationExtensions.cs │ │ │ │ │ ├── LocalApiAuthenticationHandler.cs │ │ │ │ │ └── LocalApiAuthenticationOptions.cs │ │ │ │ └── MutualTlsEndpointMiddleware.cs │ │ │ ├── IdentityServer8.csproj │ │ │ ├── IdentityServerConstants.cs │ │ │ ├── IdentityServerTools.cs │ │ │ ├── IdentityServerUser.cs │ │ │ ├── Infrastructure/ │ │ │ │ ├── DistributedCacheStateDataFormatter.cs │ │ │ │ ├── MessageCookie.cs │ │ │ │ └── ObjectSerializer.cs │ │ │ ├── Logging/ │ │ │ │ ├── LogSerializer.cs │ │ │ │ └── Models/ │ │ │ │ ├── AuthorizeRequestValidationLog.cs │ │ │ │ ├── AuthorizeResponseLog.cs │ │ │ │ ├── DeviceAuthorizationRequestValidationLog.cs │ │ │ │ ├── EndSessionRequestValidationLog.cs │ │ │ │ ├── TokenRequestValidationLog.cs │ │ │ │ └── TokenValidationLog.cs │ │ │ ├── Models/ │ │ │ │ ├── Contexts/ │ │ │ │ │ ├── IsActiveContext.cs │ │ │ │ │ ├── LogoutNotificationContext.cs │ │ │ │ │ └── ProfileDataRequestContext.cs │ │ │ │ ├── DeviceFlowAuthorizationRequest.cs │ │ │ │ ├── DeviceFlowInteractionResult.cs │ │ │ │ ├── DiscoveryDocument.cs │ │ │ │ ├── Grant.cs │ │ │ │ ├── GrantTypes.cs │ │ │ │ ├── IdentityResources.cs │ │ │ │ ├── JsonWebKey.cs │ │ │ │ ├── Messages/ │ │ │ │ │ ├── AuthorizationRequest.cs │ │ │ │ │ ├── ConsentRequest.cs │ │ │ │ │ ├── ConsentResponse.cs │ │ │ │ │ ├── ErrorMessage.cs │ │ │ │ │ ├── LogoutRequest.cs │ │ │ │ │ └── Message.cs │ │ │ │ ├── ParsedSecret.cs │ │ │ │ ├── SecurityKeyInfo.cs │ │ │ │ ├── TokenCreationRequest.cs │ │ │ │ └── TokenRequestErrors.cs │ │ │ ├── Properties/ │ │ │ │ └── AssemblyInfo.cs │ │ │ ├── ResponseHandling/ │ │ │ │ ├── Default/ │ │ │ │ │ ├── AuthorizeInteractionResponseGenerator.cs │ │ │ │ │ ├── AuthorizeResponseGenerator.cs │ │ │ │ │ ├── DeviceAuthorizationResponseGenerator.cs │ │ │ │ │ ├── DiscoveryResponseGenerator.cs │ │ │ │ │ ├── IntrospectionResponseGenerator.cs │ │ │ │ │ ├── TokenResponseGenerator.cs │ │ │ │ │ ├── TokenRevocationResponseGenerator.cs │ │ │ │ │ └── UserInfoResponseGenerator.cs │ │ │ │ ├── IAuthorizeInteractionResponseGenerator.cs │ │ │ │ ├── IAuthorizeResponseGenerator.cs │ │ │ │ ├── IDeviceAuthorizationResponseGenerator.cs │ │ │ │ ├── IDiscoveryResponseGenerator.cs │ │ │ │ ├── IIntrospectionResponseGenerator.cs │ │ │ │ ├── ITokenResponseGenerator.cs │ │ │ │ ├── ITokenRevocationResponseGenerator.cs │ │ │ │ ├── IUserInfoResponseGenerator.cs │ │ │ │ └── Models/ │ │ │ │ ├── AuthorizeResponse.cs │ │ │ │ ├── DeviceAuthorizationResponse.cs │ │ │ │ ├── InteractionResponse.cs │ │ │ │ ├── TokenErrorResponse.cs │ │ │ │ ├── TokenResponse.cs │ │ │ │ └── TokenRevocationResponse.cs │ │ │ ├── Services/ │ │ │ │ ├── Default/ │ │ │ │ │ ├── BackChannelLogoutHttpClient.cs │ │ │ │ │ ├── DefaultBackChannelLogoutService.cs │ │ │ │ │ ├── DefaultCache.cs │ │ │ │ │ ├── DefaultClaimsService.cs │ │ │ │ │ ├── DefaultConsentService.cs │ │ │ │ │ ├── DefaultCorsPolicyService.cs │ │ │ │ │ ├── DefaultDeviceFlowCodeService.cs │ │ │ │ │ ├── DefaultDeviceFlowInteractionService.cs │ │ │ │ │ ├── DefaultEventService.cs │ │ │ │ │ ├── DefaultEventSink.cs │ │ │ │ │ ├── DefaultHandleGenerationService.cs │ │ │ │ │ ├── DefaultIdentityServerInteractionService.cs │ │ │ │ │ ├── DefaultJwtRequestUriHttpClient.cs │ │ │ │ │ ├── DefaultKeyMaterialService.cs │ │ │ │ │ ├── DefaultPersistedGrantService.cs │ │ │ │ │ ├── DefaultProfileService.cs │ │ │ │ │ ├── DefaultRefreshTokenService.cs │ │ │ │ │ ├── DefaultReplayCache.cs │ │ │ │ │ ├── DefaultTokenCreationService.cs │ │ │ │ │ ├── DefaultTokenService.cs │ │ │ │ │ ├── DefaultUserCodeService.cs │ │ │ │ │ ├── DefaultUserSession.cs │ │ │ │ │ ├── DistributedDeviceFlowThrottlingService.cs │ │ │ │ │ ├── LogoutNotificationService.cs │ │ │ │ │ ├── NumericUserCodeGenerator.cs │ │ │ │ │ ├── OidcReturnUrlParser.cs │ │ │ │ │ └── ReturnUrlParser.cs │ │ │ │ ├── IBackChannelLogoutHttpClient.cs │ │ │ │ ├── IBackChannelLogoutService.cs │ │ │ │ ├── ICache.cs │ │ │ │ ├── IClaimsService.cs │ │ │ │ ├── IConsentService.cs │ │ │ │ ├── IDeviceFlowCodeService.cs │ │ │ │ ├── IDeviceFlowInteractionService.cs │ │ │ │ ├── IDeviceFlowThrottlingService.cs │ │ │ │ ├── IEventService.cs │ │ │ │ ├── IEventSink.cs │ │ │ │ ├── IHandleGenerationService.cs │ │ │ │ ├── IIdentityServerInteractionService.cs │ │ │ │ ├── IJwtRequestUriHttpClient.cs │ │ │ │ ├── IKeyMaterialService.cs │ │ │ │ ├── ILogoutNotificationService.cs │ │ │ │ ├── IPersistedGrantService.cs │ │ │ │ ├── IProfileService.cs │ │ │ │ ├── IRefreshTokenService.cs │ │ │ │ ├── IReplayCache.cs │ │ │ │ ├── IReturnUrlParser.cs │ │ │ │ ├── ITokenCreationService.cs │ │ │ │ ├── ITokenService.cs │ │ │ │ ├── IUserCodeGenerator.cs │ │ │ │ ├── IUserCodeService.cs │ │ │ │ ├── IUserSession.cs │ │ │ │ └── InMemory/ │ │ │ │ └── InMemoryCorsPolicyService.cs │ │ │ ├── Stores/ │ │ │ │ ├── Caching/ │ │ │ │ │ ├── CachingClientStore.cs │ │ │ │ │ ├── CachingCorsPolicyService.cs │ │ │ │ │ └── CachingResourceStore.cs │ │ │ │ ├── Default/ │ │ │ │ │ ├── ConsentMessageStore.cs │ │ │ │ │ ├── DefaultAuthorizationCodeStore.cs │ │ │ │ │ ├── DefaultGrantStore.cs │ │ │ │ │ ├── DefaultReferenceTokenStore.cs │ │ │ │ │ ├── DefaultRefreshTokenStore.cs │ │ │ │ │ ├── DefaultUserConsentStore.cs │ │ │ │ │ ├── DistributedCacheAuthorizationParametersMessageStore.cs │ │ │ │ │ ├── ProtectedDataMessageStore.cs │ │ │ │ │ └── QueryStringAuthorizationParametersMessageStore.cs │ │ │ │ ├── IAuthorizationParametersMessageStore.cs │ │ │ │ ├── IConsentMessageStore.cs │ │ │ │ ├── IMessageStore.cs │ │ │ │ ├── ISigningCredentialStore.cs │ │ │ │ ├── IValidationKeysStore.cs │ │ │ │ ├── InMemory/ │ │ │ │ │ ├── InMemoryClientStore.cs │ │ │ │ │ ├── InMemoryDeviceFlowStore.cs │ │ │ │ │ ├── InMemoryPersistedGrantStore.cs │ │ │ │ │ ├── InMemoryResourcesStore.cs │ │ │ │ │ ├── InMemorySigningCredentialsStore.cs │ │ │ │ │ └── InMemoryValidationKeysStore.cs │ │ │ │ └── ValidatingClientStore.cs │ │ │ ├── Test/ │ │ │ │ ├── IdentityServerBuilderExtensions.cs │ │ │ │ ├── TestUser.cs │ │ │ │ ├── TestUserProfileService.cs │ │ │ │ ├── TestUserResourceOwnerPasswordValidator.cs │ │ │ │ └── TestUserStore.cs │ │ │ └── Validation/ │ │ │ ├── Contexts/ │ │ │ │ ├── ClientConfigurationValidationContext.cs │ │ │ │ ├── CustomAuthorizeRequestValidationContext.cs │ │ │ │ ├── CustomTokenRequestValidationContext.cs │ │ │ │ ├── ExtensionGrantValidationContext.cs │ │ │ │ ├── ResourceOwnerPasswordValidationContext.cs │ │ │ │ └── ResourceValidationContext.cs │ │ │ ├── Default/ │ │ │ │ ├── ApiSecretValidator.cs │ │ │ │ ├── AuthorizeRequestValidator.cs │ │ │ │ ├── BasicAuthenticationSecretParser.cs │ │ │ │ ├── BearerTokenUsageValidator.cs │ │ │ │ ├── ClientSecretValidator.cs │ │ │ │ ├── DefaultClientConfigurationValidator.cs │ │ │ │ ├── DefaultCustomAuthorizeRequestValidator.cs │ │ │ │ ├── DefaultCustomTokenRequestValidator.cs │ │ │ │ ├── DefaultCustomTokenValidator.cs │ │ │ │ ├── DefaultResourceValidator.cs │ │ │ │ ├── DefaultScopeParser.cs │ │ │ │ ├── DeviceAuthorizationRequestValidator.cs │ │ │ │ ├── DeviceCodeValidator.cs │ │ │ │ ├── EndSessionRequestValidator.cs │ │ │ │ ├── ExtensionGrantValidator.cs │ │ │ │ ├── HashedSharedSecretValidator.cs │ │ │ │ ├── IntrospectionRequestValidator.cs │ │ │ │ ├── JwtBearerClientAssertionSecretParser.cs │ │ │ │ ├── JwtRequestValidator.cs │ │ │ │ ├── MutualTlsSecretParser.cs │ │ │ │ ├── NopClientConfigurationValidator.cs │ │ │ │ ├── NotSupportedResouceOwnerCredentialValidator.cs │ │ │ │ ├── PlainTextSharedSecretValidator.cs │ │ │ │ ├── PostBodySecretParser.cs │ │ │ │ ├── PrivateKeyJwtSecretValidator.cs │ │ │ │ ├── ResponseTypeEqualityComparer.cs │ │ │ │ ├── SecretParser.cs │ │ │ │ ├── SecretValidator.cs │ │ │ │ ├── StrictRedirectUriValidator.cs │ │ │ │ ├── StrictRedirectUriValidatorAppAuth.cs │ │ │ │ ├── TokenRequestValidator.cs │ │ │ │ ├── TokenRevocationRequestValidator.cs │ │ │ │ ├── TokenValidator.cs │ │ │ │ ├── UserInfoRequestValidator.cs │ │ │ │ ├── X509NameSecretValidator.cs │ │ │ │ └── X509ThumbprintSecretValidator.cs │ │ │ ├── IApiSecretValidator.cs │ │ │ ├── IAuthorizeRequestValidator.cs │ │ │ ├── IClientConfigurationValidator.cs │ │ │ ├── IClientSecretValidator.cs │ │ │ ├── ICustomAuthorizeRequestValidator.cs │ │ │ ├── ICustomTokenRequestValidator.cs │ │ │ ├── ICustomTokenValidator.cs │ │ │ ├── IDeviceAuthorizationRequestValidator.cs │ │ │ ├── IDeviceCodeValidator.cs │ │ │ ├── IEndSessionRequestValidator.cs │ │ │ ├── IExtensionGrantValidator.cs │ │ │ ├── IIntrospectionRequestValidator.cs │ │ │ ├── IRedirectUriValidator.cs │ │ │ ├── IResourceOwnerPasswordValidator.cs │ │ │ ├── IResourceValidator.cs │ │ │ ├── IScopeParser.cs │ │ │ ├── ISecretParser.cs │ │ │ ├── ISecretValidator.cs │ │ │ ├── ISecretsListParser.cs │ │ │ ├── ISecretsListValidator.cs │ │ │ ├── ITokenRequestValidator.cs │ │ │ ├── ITokenRevocationRequestValidator.cs │ │ │ ├── ITokenValidator.cs │ │ │ ├── IUserInfoRequestValidator.cs │ │ │ └── Models/ │ │ │ ├── AuthorizeRequestValidationResult.cs │ │ │ ├── BearerTokenUsageType.cs │ │ │ ├── BearerTokenUsageValidationResult.cs │ │ │ ├── ClientSecretValidationResult.cs │ │ │ ├── DeviceAuthorizationRequestValidationResult.cs │ │ │ ├── DeviceCodeValidationContext.cs │ │ │ ├── EndSessionCallbackValidationResult.cs │ │ │ ├── EndSessionValidationResult.cs │ │ │ ├── GrantValidationResult.cs │ │ │ ├── IntrospectionRequestValidationResult.cs │ │ │ ├── JwtRequestValidationResult.cs │ │ │ ├── ParsedScopeValidationError.cs │ │ │ ├── ParsedScopeValue.cs │ │ │ ├── ParsedScopesResult.cs │ │ │ ├── ResourceValidationRequest.cs │ │ │ ├── ResourceValidationResult.cs │ │ │ ├── ScopeSecretValidationResult.cs │ │ │ ├── SecretValidationResult.cs │ │ │ ├── TokenRequestValidationResult.cs │ │ │ ├── TokenRevocationRequestValidationResult.cs │ │ │ ├── TokenValidationResult.cs │ │ │ ├── UserInfoRequestValidationResult.cs │ │ │ ├── ValidatedAuthorizeRequest.cs │ │ │ ├── ValidatedDeviceAuthorizationRequest.cs │ │ │ ├── ValidatedEndSessionRequest.cs │ │ │ ├── ValidatedRequest.cs │ │ │ ├── ValidatedTokenRequest.cs │ │ │ └── ValidationResult.cs │ │ └── test/ │ │ ├── IdentityServer.IntegrationTests/ │ │ │ ├── Clients/ │ │ │ │ ├── ClientAssertionClient.cs │ │ │ │ ├── ClientCredentialsAndResourceOwnerClient.cs │ │ │ │ ├── ClientCredentialsClient.cs │ │ │ │ ├── CustomTokenRequestValidatorClient.cs │ │ │ │ ├── CustomTokenResponseClients.cs │ │ │ │ ├── DiscoveryClient.cs │ │ │ │ ├── ExtensionGrantClient.cs │ │ │ │ ├── RefreshTokenClient.cs │ │ │ │ ├── ResourceOwnerClient.cs │ │ │ │ ├── RevocationClient.cs │ │ │ │ ├── Setup/ │ │ │ │ │ ├── Clients.cs │ │ │ │ │ ├── ConfirmationSecretValidator.cs │ │ │ │ │ ├── CustomProfileService.cs │ │ │ │ │ ├── CustomResponseDto.cs │ │ │ │ │ ├── CustomResponseExtensionGrantValidator.cs │ │ │ │ │ ├── CustomResponseResourceOwnerValidator.cs │ │ │ │ │ ├── DynamicParameterExtensionGrantValidator.cs │ │ │ │ │ ├── ExtensionGrantValidator.cs │ │ │ │ │ ├── ExtensionGrantValidator2.cs │ │ │ │ │ ├── NoSubjectExtensionGrantValidator.cs │ │ │ │ │ ├── Scopes.cs │ │ │ │ │ ├── Startup.cs │ │ │ │ │ ├── StartupWithCustomTokenResponses.cs │ │ │ │ │ ├── TestCustomTokenRequestValidator.cs │ │ │ │ │ └── Users.cs │ │ │ │ └── UserInfoClient.cs │ │ │ ├── Common/ │ │ │ │ ├── BrowserClient.cs │ │ │ │ ├── BrowserHandler.cs │ │ │ │ ├── IdentityServerPipeline.cs │ │ │ │ ├── MessageHandlerWrapper.cs │ │ │ │ ├── NetworkHandler.cs │ │ │ │ └── TestCert.cs │ │ │ ├── Conformance/ │ │ │ │ ├── Basic/ │ │ │ │ │ ├── ClientAuthenticationTests.cs │ │ │ │ │ ├── CodeFlowTests.cs │ │ │ │ │ ├── RedirectUriTests.cs │ │ │ │ │ └── ResponseTypeResponseModeTests.cs │ │ │ │ └── Pkce/ │ │ │ │ └── PkceTests.cs │ │ │ ├── Endpoints/ │ │ │ │ ├── Authorize/ │ │ │ │ │ ├── AuthorizeTests.cs │ │ │ │ │ ├── ConsentTests.cs │ │ │ │ │ ├── JwtRequestAuthorizeTests.cs │ │ │ │ │ ├── RestrictAccessTokenViaBrowserTests.cs │ │ │ │ │ └── SessionIdTests.cs │ │ │ │ ├── CheckSession/ │ │ │ │ │ └── CheckSessionTests.cs │ │ │ │ ├── DeviceAuthorization/ │ │ │ │ │ └── DeviceAuthorizationTests.cs │ │ │ │ ├── Discovery/ │ │ │ │ │ └── DiscoveryEndpointTests.cs │ │ │ │ ├── EndSession/ │ │ │ │ │ └── EndSessionTests.cs │ │ │ │ ├── Introspection/ │ │ │ │ │ ├── IntrospectionTests.cs │ │ │ │ │ └── Setup/ │ │ │ │ │ ├── Clients.cs │ │ │ │ │ ├── Scopes.cs │ │ │ │ │ ├── Startup.cs │ │ │ │ │ └── Users.cs │ │ │ │ ├── Revocation/ │ │ │ │ │ └── RevocationTests.cs │ │ │ │ └── Token/ │ │ │ │ └── TokenEndpointTests.cs │ │ │ ├── Extensibility/ │ │ │ │ └── CustomProfileServiceTests.cs │ │ │ ├── IdentityServer.IntegrationTests.csproj │ │ │ ├── Pipeline/ │ │ │ │ ├── CorsTests.cs │ │ │ │ ├── FederatedSignoutTests.cs │ │ │ │ └── SubpathHosting.cs │ │ │ ├── identityserver_testing.cer │ │ │ └── xunit.runner.json │ │ └── IdentityServer.UnitTests/ │ │ ├── Common/ │ │ │ ├── IAuthenticationSchemeHandler.cs │ │ │ ├── MockAuthenticationHandler.cs │ │ │ ├── MockAuthenticationHandlerProvider.cs │ │ │ ├── MockAuthenticationSchemeProvider.cs │ │ │ ├── MockAuthenticationService.cs │ │ │ ├── MockClaimsService.cs │ │ │ ├── MockClientSessionService.cs │ │ │ ├── MockConsentMessageStore.cs │ │ │ ├── MockConsentService.cs │ │ │ ├── MockHttpContextAccessor.cs │ │ │ ├── MockKeyMaterialService.cs │ │ │ ├── MockLogoutNotificationService.cs │ │ │ ├── MockMessageStore.cs │ │ │ ├── MockPersistedGrantService.cs │ │ │ ├── MockProfileService.cs │ │ │ ├── MockReferenceTokenStore.cs │ │ │ ├── MockResourceValidator.cs │ │ │ ├── MockReturnUrlParser.cs │ │ │ ├── MockSessionIdService.cs │ │ │ ├── MockSystemClock.cs │ │ │ ├── MockTokenCreationService.cs │ │ │ ├── MockUserSession.cs │ │ │ ├── NetworkHandler.cs │ │ │ ├── StubAuthorizeResponseGenerator.cs │ │ │ ├── StubClock.cs │ │ │ ├── StubHandleGenerationService.cs │ │ │ ├── TestCert.cs │ │ │ ├── TestEventService.cs │ │ │ ├── TestExtensions.cs │ │ │ ├── TestIdentityServerOptions.cs │ │ │ ├── TestLogger.cs │ │ │ └── TestUserConsentStore.cs │ │ ├── Cors/ │ │ │ ├── MockCorsPolicyProvider.cs │ │ │ ├── MockCorsPolicyService.cs │ │ │ └── PolicyProviderTests.cs │ │ ├── Endpoints/ │ │ │ ├── Authorize/ │ │ │ │ ├── AuthorizeCallbackEndpointTests.cs │ │ │ │ ├── AuthorizeEndpointBaseTests.cs │ │ │ │ ├── AuthorizeEndpointTests.cs │ │ │ │ ├── StubAuthorizeInteractionResponseGenerator.cs │ │ │ │ └── StubAuthorizeRequestValidator.cs │ │ │ ├── EndSession/ │ │ │ │ ├── EndSessionCallbackEndpointTests.cs │ │ │ │ ├── EndSessionCallbackResultTests.cs │ │ │ │ ├── StubBackChannelLogoutClient.cs │ │ │ │ └── StubEndSessionRequestValidator.cs │ │ │ └── Results/ │ │ │ ├── AuthorizeResultTests.cs │ │ │ ├── CheckSessionResultTests.cs │ │ │ ├── EndSessionCallbackResultTests.cs │ │ │ └── EndSessionResultTests.cs │ │ ├── Extensions/ │ │ │ ├── ApiResourceSigningAlgorithmSelectionTests.cs │ │ │ ├── EndpointOptionsExtensionsTests.cs │ │ │ ├── HttpRequestExtensionsTests.cs │ │ │ ├── IResourceStoreExtensionsTests.cs │ │ │ ├── IdentityServerBuilderExtensionsCacheStoreTests.cs │ │ │ ├── IdentityServerBuilderExtensionsCryptoTests.cs │ │ │ ├── JwtPayloadCreationTests.cs │ │ │ ├── StringExtensionsTests.cs │ │ │ └── ValidatedAuthorizeRequestExtensionsTests.cs │ │ ├── Hosting/ │ │ │ └── EndpointRouterTests.cs │ │ ├── IdentityServer.UnitTests.csproj │ │ ├── Infrastructure/ │ │ │ └── ObjectSerializerTests.cs │ │ ├── ResponseHandling/ │ │ │ ├── AuthorizeInteractionResponseGenerator/ │ │ │ │ ├── AuthorizeInteractionResponseGeneratorTests.cs │ │ │ │ ├── AuthorizeInteractionResponseGeneratorTests_Consent.cs │ │ │ │ ├── AuthorizeInteractionResponseGeneratorTests_Custom.cs │ │ │ │ └── AuthorizeInteractionResponseGeneratorTests_Login.cs │ │ │ ├── DeviceAuthorizationResponseGeneratorTests.cs │ │ │ └── UserInfoResponseGeneratorTests.cs │ │ ├── Services/ │ │ │ ├── Default/ │ │ │ │ ├── DefaultClaimsServiceTests.cs │ │ │ │ ├── DefaultConsentServiceTests.cs │ │ │ │ ├── DefaultCorsPolicyServiceTests.cs │ │ │ │ ├── DefaultIdentityServerInteractionServiceTests.cs │ │ │ │ ├── DefaultPersistedGrantServiceTests.cs │ │ │ │ ├── DefaultRefreshTokenServiceTests.cs │ │ │ │ ├── DefaultTokenServiceTests.cs │ │ │ │ ├── DefaultUserSessionTests.cs │ │ │ │ ├── DistributedDeviceFlowThrottlingServiceTests.cs │ │ │ │ └── NumericUserCodeServiceTests.cs │ │ │ └── InMemory/ │ │ │ └── InMemoryCorsPolicyService.cs │ │ ├── Stores/ │ │ │ ├── Default/ │ │ │ │ └── DefaultPersistedGrantStoreTests.cs │ │ │ ├── InMemoryClientStoreTests.cs │ │ │ ├── InMemoryDeviceFlowStoreTests.cs │ │ │ ├── InMemoryPersistedGrantStoreTests.cs │ │ │ └── InMemoryResourcesStoreTests.cs │ │ ├── Validation/ │ │ │ ├── AccessTokenValidation.cs │ │ │ ├── AuthorizeRequest Validation/ │ │ │ │ ├── Authorize_ClientValidation_Code.cs │ │ │ │ ├── Authorize_ClientValidation_IdToken.cs │ │ │ │ ├── Authorize_ClientValidation_Invalid.cs │ │ │ │ ├── Authorize_ClientValidation_Token.cs │ │ │ │ ├── Authorize_ClientValidation_Valid.cs │ │ │ │ ├── Authorize_ProtocolValidation_CustomValidator.cs │ │ │ │ ├── Authorize_ProtocolValidation_Invalid.cs │ │ │ │ ├── Authorize_ProtocolValidation_PKCE.cs │ │ │ │ └── Authorize_ProtocolValidation_Valid.cs │ │ │ ├── BearerTokenUsageValidation.cs │ │ │ ├── ClientConfigurationValidation.cs │ │ │ ├── DeviceAuthorizationRequestValidation.cs │ │ │ ├── DeviceCodeValidation.cs │ │ │ ├── EndSessionRequestValidation/ │ │ │ │ ├── EndSessionRequestValidatorTests.cs │ │ │ │ ├── StubRedirectUriValidator.cs │ │ │ │ └── StubTokenValidator.cs │ │ │ ├── GrantTypesValidation.cs │ │ │ ├── IdentityTokenValidation.cs │ │ │ ├── IntrospectionRequestValidatorTests.cs │ │ │ ├── ResourceValidation.cs │ │ │ ├── ResponseTypeEqualityComparison.cs │ │ │ ├── RevocationRequestValidation.cs │ │ │ ├── Secrets/ │ │ │ │ ├── BasicAuthenticationCredentialParsing.cs │ │ │ │ ├── ClientAssertionSecretParsing.cs │ │ │ │ ├── ClientSecretValidation.cs │ │ │ │ ├── FormPostCredentialParsing.cs │ │ │ │ ├── HashedSharedSecretValidation.cs │ │ │ │ ├── MutualTlsSecretValidation.cs │ │ │ │ ├── PlainTextClientSecretValidation.cs │ │ │ │ ├── PrivateKeyJwtSecretValidation.cs │ │ │ │ └── SecretValidation.cs │ │ │ ├── Setup/ │ │ │ │ ├── ClientValidationTestClients.cs │ │ │ │ ├── Factory.cs │ │ │ │ ├── TestClients.cs │ │ │ │ ├── TestDeviceCodeValidator.cs │ │ │ │ ├── TestDeviceFlowThrottlingService.cs │ │ │ │ ├── TestGrantValidator.cs │ │ │ │ ├── TestProfileService.cs │ │ │ │ ├── TestResourceOwnerPasswordValidator.cs │ │ │ │ ├── TestScopes.cs │ │ │ │ ├── TestTokenValidator.cs │ │ │ │ ├── TokenFactory.cs │ │ │ │ └── ValidationExtensions.cs │ │ │ ├── StrictRedirectUriValidatorAppAuthValidation.cs │ │ │ ├── TokenRequest Validation/ │ │ │ │ ├── TokenRequestValidation_ClientCredentials_Invalid.cs │ │ │ │ ├── TokenRequestValidation_Code_Invalid.cs │ │ │ │ ├── TokenRequestValidation_DeviceCode_Invalid.cs │ │ │ │ ├── TokenRequestValidation_ExtensionGrants_Invalid.cs │ │ │ │ ├── TokenRequestValidation_General_Invalid.cs │ │ │ │ ├── TokenRequestValidation_PKCE.cs │ │ │ │ ├── TokenRequestValidation_RefreshToken_Invalid.cs │ │ │ │ ├── TokenRequestValidation_ResourceOwner_Invalid.cs │ │ │ │ └── TokenRequestValidation_Valid.cs │ │ │ └── UserInfoRequestValidation.cs │ │ ├── identityserver_testing.cer │ │ └── xunit.runner.json │ ├── IdentityServer8.sln │ ├── IdentityServer8.sln.licenseheader │ ├── Security/ │ │ ├── Directory.Build.props │ │ ├── IdentityServer8.Security/ │ │ │ ├── Extensions.cs │ │ │ ├── GlobalUsings.cs │ │ │ ├── IdentityServer8.Security.csproj │ │ │ ├── RedirectService.cs │ │ │ ├── RedirectUrlParser.cs │ │ │ ├── RedirectUrlServiceExtensions.cs │ │ │ ├── Sanitizer.cs │ │ │ └── SanitizerServiceExtensions.cs │ │ └── test/ │ │ └── IdentityServer8.Santizer.Tests/ │ │ ├── IdentityServer8.Sanitizer.Tests.csproj │ │ ├── RedirectServiceTests.cs │ │ └── Services/ │ │ └── SanitizerTests.cs │ └── Storage/ │ ├── Directory.Build.props │ ├── IdentityServer8.Storage.sln │ ├── README.md │ ├── build/ │ │ ├── Program.cs │ │ └── build.csproj │ ├── build.cmd │ ├── build.ps1 │ ├── build.sh │ └── src/ │ ├── Constants.cs │ ├── Extensions/ │ │ ├── IEnumerableExtensions.cs │ │ ├── PersistedGrantFilterExtensions.cs │ │ └── StringsExtensions.cs │ ├── GlobalUsings.cs │ ├── IdentityServer8.Storage.csproj │ ├── IdentityServerConstants.cs │ ├── IdentityServerUser.cs │ ├── Models/ │ │ ├── ApiResource.cs │ │ ├── ApiScope.cs │ │ ├── AuthorizationCode.cs │ │ ├── Client.cs │ │ ├── ClientClaim.cs │ │ ├── Consent.cs │ │ ├── DeviceCode.cs │ │ ├── Enums.cs │ │ ├── GrantType.cs │ │ ├── IdentityResource.cs │ │ ├── PersistedGrant.cs │ │ ├── RefreshToken.cs │ │ ├── Resource.cs │ │ ├── Resources.cs │ │ ├── Secret.cs │ │ └── Token.cs │ ├── Services/ │ │ └── ICorsPolicyService.cs │ └── Stores/ │ ├── IAuthorizationCodeStore.cs │ ├── IClientStore.cs │ ├── IDeviceFlowStore.cs │ ├── IPersistedGrantStore.cs │ ├── IReferenceTokenStore.cs │ ├── IRefreshTokenStore.cs │ ├── IResourceStore.cs │ ├── IUserConsentStore.cs │ ├── PersistedGrantFilter.cs │ └── Serialization/ │ ├── ClaimConverter.cs │ ├── ClaimLite.cs │ ├── ClaimsPrincipalConverter.cs │ ├── ClaimsPrincipalLite.cs │ ├── CustomContractResolver.cs │ ├── IPersistentGrantSerializer.cs │ └── PersistentGrantSerializer.cs └── version.json ================================================ FILE CONTENTS ================================================ ================================================ FILE: .config/dotnet-tools.json ================================================ { "version": 1, "isRoot": true, "tools": { "signclient": { "version": "1.2.17", "commands": [ "SignClient" ] }, "dotnet-ef": { "version": "3.1.0", "commands": [ "dotnet-ef" ] } } } ================================================ FILE: .editorconfig ================================================ root=true # Remove the line below if you want to inherit .editorconfig settings from higher directories # C# files [*.cs] #### Core EditorConfig Options #### # Indentation and spacing indent_size=4 indent_style=space tab_width=4 # New line preferences end_of_line=crlf insert_final_newline=false #### .NET Coding Conventions #### # this. and Me. preferences dotnet_style_qualification_for_event=false:warning dotnet_style_qualification_for_field=false:warning dotnet_style_qualification_for_method=false:warning dotnet_style_qualification_for_property=false:warning # Language keywords vs BCL types preferences dotnet_style_predefined_type_for_locals_parameters_members=true:silent dotnet_style_predefined_type_for_member_access=true:silent # Parentheses preferences dotnet_style_parentheses_in_arithmetic_binary_operators=always_for_clarity:silent dotnet_style_parentheses_in_other_binary_operators=always_for_clarity:silent dotnet_style_parentheses_in_other_operators=never_if_unnecessary:silent dotnet_style_parentheses_in_relational_binary_operators=always_for_clarity:silent # Modifier preferences dotnet_style_require_accessibility_modifiers=for_non_interface_members:silent # Expression-level preferences csharp_style_deconstructed_variable_declaration=true:suggestion csharp_style_inlined_variable_declaration=true:suggestion csharp_style_throw_expression=true:suggestion dotnet_style_coalesce_expression=true:suggestion dotnet_style_collection_initializer=true:suggestion dotnet_style_explicit_tuple_names=true:suggestion dotnet_style_null_propagation=true:suggestion dotnet_style_object_initializer=true:suggestion dotnet_style_prefer_auto_properties=true:silent dotnet_style_prefer_compound_assignment=true:suggestion dotnet_style_prefer_conditional_expression_over_assignment=true:silent dotnet_style_prefer_conditional_expression_over_return=true:silent dotnet_style_prefer_inferred_anonymous_type_member_names=true:suggestion dotnet_style_prefer_inferred_tuple_names=true:suggestion dotnet_style_prefer_is_null_check_over_reference_equality_method=true:suggestion # Field preferences dotnet_style_readonly_field=true:suggestion # Parameter preferences dotnet_code_quality_unused_parameters=all:suggestion #### C# Coding Conventions #### # var preferences csharp_style_var_elsewhere=false:silent csharp_style_var_for_built_in_types=false:silent csharp_style_var_when_type_is_apparent=false:silent # Expression-bodied members csharp_style_expression_bodied_accessors=true:silent csharp_style_expression_bodied_constructors=false:silent csharp_style_expression_bodied_indexers=true:silent csharp_style_expression_bodied_lambdas=true:silent csharp_style_expression_bodied_local_functions=false:silent csharp_style_expression_bodied_methods=false:silent csharp_style_expression_bodied_operators=false:silent csharp_style_expression_bodied_properties=true:silent # Pattern matching preferences csharp_style_pattern_matching_over_as_with_null_check=true:suggestion csharp_style_pattern_matching_over_is_with_cast_check=true:suggestion # Null-checking preferences csharp_style_conditional_delegate_call=true:suggestion # Modifier preferences csharp_preferred_modifier_order=public,private,protected,internal,static,extern,new,virtual,abstract,sealed,override,readonly,unsafe,volatile,async # Code-block preferences csharp_prefer_braces=true:silent # Expression-level preferences csharp_prefer_simple_default_expression=true:suggestion csharp_style_pattern_local_over_anonymous_function=true:suggestion csharp_style_prefer_index_operator=true:suggestion csharp_style_prefer_range_operator=true:suggestion csharp_style_unused_value_assignment_preference=discard_variable:suggestion csharp_style_unused_value_expression_statement_preference=discard_variable:silent #### C# Formatting Rules #### # New line preferences csharp_new_line_before_catch=true csharp_new_line_before_else=true csharp_new_line_before_finally=true csharp_new_line_before_members_in_anonymous_types=true csharp_new_line_before_members_in_object_initializers=true csharp_new_line_before_open_brace=all csharp_new_line_between_query_expression_clauses=true # Indentation preferences csharp_indent_block_contents=true csharp_indent_braces=false csharp_indent_case_contents=true csharp_indent_case_contents_when_block=true csharp_indent_labels=one_less_than_current csharp_indent_switch_labels=true # Space preferences csharp_space_after_cast=false csharp_space_after_colon_in_inheritance_clause=true csharp_space_after_comma=true csharp_space_after_dot=false csharp_space_after_keywords_in_control_flow_statements=true csharp_space_after_semicolon_in_for_statement=true csharp_space_around_binary_operators=before_and_after csharp_space_around_declaration_statements=false csharp_space_before_colon_in_inheritance_clause=true csharp_space_before_comma=false csharp_space_before_dot=false csharp_space_before_open_square_brackets=false csharp_space_before_semicolon_in_for_statement=false csharp_space_between_empty_square_brackets=false csharp_space_between_method_call_empty_parameter_list_parentheses=false csharp_space_between_method_call_name_and_opening_parenthesis=false csharp_space_between_method_call_parameter_list_parentheses=false csharp_space_between_method_declaration_empty_parameter_list_parentheses=false csharp_space_between_method_declaration_name_and_open_parenthesis=false csharp_space_between_method_declaration_parameter_list_parentheses=false csharp_space_between_parentheses=false csharp_space_between_square_brackets=false # Wrapping preferences csharp_preserve_single_line_blocks=true csharp_preserve_single_line_statements=true [*] charset=utf-8 end_of_line=lf trim_trailing_whitespace=false insert_final_newline=false indent_style=space indent_size=4 # Microsoft .NET properties csharp_indent_braces=false csharp_indent_switch_labels=true csharp_new_line_before_catch=true csharp_new_line_before_else=true csharp_new_line_before_finally=true csharp_new_line_before_members_in_object_initializers=false csharp_new_line_before_open_brace=all csharp_new_line_between_query_expression_clauses=true csharp_preferred_modifier_order=public, private, protected, internal, new, abstract, virtual, sealed, override, static, readonly, extern, unsafe, volatile, async:suggestion csharp_preserve_single_line_blocks=true csharp_space_after_cast=true csharp_space_after_colon_in_inheritance_clause=true csharp_space_after_comma=true csharp_space_after_dot=false csharp_space_after_keywords_in_control_flow_statements=true csharp_space_after_semicolon_in_for_statement=true csharp_space_around_binary_operators=before_and_after csharp_space_before_colon_in_inheritance_clause=true csharp_space_before_comma=false csharp_space_before_dot=false csharp_space_before_open_square_brackets=false csharp_space_before_semicolon_in_for_statement=false csharp_space_between_empty_square_brackets=false csharp_space_between_method_call_empty_parameter_list_parentheses=false csharp_space_between_method_call_name_and_opening_parenthesis=false csharp_space_between_method_call_parameter_list_parentheses=false csharp_space_between_method_declaration_empty_parameter_list_parentheses=false csharp_space_between_method_declaration_name_and_open_parenthesis=false csharp_space_between_method_declaration_parameter_list_parentheses=false csharp_space_between_parentheses=false csharp_space_between_square_brackets=false csharp_style_var_elsewhere=true:suggestion csharp_style_var_for_built_in_types=true:suggestion csharp_style_var_when_type_is_apparent=true:suggestion csharp_using_directive_placement=outside_namespace:silent dotnet_naming_rule.constants_rule.severity=warning dotnet_naming_rule.constants_rule.style=upper_camel_case_style dotnet_naming_rule.constants_rule.symbols=constants_symbols dotnet_naming_rule.event_rule.severity=warning dotnet_naming_rule.event_rule.style=upper_camel_case_style dotnet_naming_rule.event_rule.symbols=event_symbols dotnet_naming_rule.interfaces_rule.severity=warning dotnet_naming_rule.interfaces_rule.style=i_upper_camel_case_style dotnet_naming_rule.interfaces_rule.symbols=interfaces_symbols dotnet_naming_rule.locals_rule.severity=warning dotnet_naming_rule.locals_rule.style=lower_camel_case_style_1 dotnet_naming_rule.locals_rule.symbols=locals_symbols dotnet_naming_rule.local_constants_rule.severity=warning dotnet_naming_rule.local_constants_rule.style=lower_camel_case_style_1 dotnet_naming_rule.local_constants_rule.symbols=local_constants_symbols dotnet_naming_rule.local_functions_rule.severity=warning dotnet_naming_rule.local_functions_rule.style=upper_camel_case_style dotnet_naming_rule.local_functions_rule.symbols=local_functions_symbols dotnet_naming_rule.method_rule.severity=warning dotnet_naming_rule.method_rule.style=upper_camel_case_style dotnet_naming_rule.method_rule.symbols=method_symbols dotnet_naming_rule.parameters_rule.severity=warning dotnet_naming_rule.parameters_rule.style=lower_camel_case_style_1 dotnet_naming_rule.parameters_rule.symbols=parameters_symbols dotnet_naming_rule.private_constants_rule.severity=warning dotnet_naming_rule.private_constants_rule.style=upper_camel_case_style dotnet_naming_rule.private_constants_rule.symbols=private_constants_symbols dotnet_naming_rule.private_instance_fields_rule.severity=warning dotnet_naming_rule.private_instance_fields_rule.style=lower_camel_case_style dotnet_naming_rule.private_instance_fields_rule.symbols=private_instance_fields_symbols dotnet_naming_rule.private_static_fields_rule.severity=warning dotnet_naming_rule.private_static_fields_rule.style=lower_camel_case_style dotnet_naming_rule.private_static_fields_rule.symbols=private_static_fields_symbols dotnet_naming_rule.private_static_readonly_rule.severity=warning dotnet_naming_rule.private_static_readonly_rule.style=upper_camel_case_style dotnet_naming_rule.private_static_readonly_rule.symbols=private_static_readonly_symbols dotnet_naming_rule.property_rule.severity=warning dotnet_naming_rule.property_rule.style=upper_camel_case_style dotnet_naming_rule.property_rule.symbols=property_symbols dotnet_naming_rule.public_fields_rule.severity=warning dotnet_naming_rule.public_fields_rule.style=upper_camel_case_style dotnet_naming_rule.public_fields_rule.symbols=public_fields_symbols dotnet_naming_rule.static_readonly_rule.severity=warning dotnet_naming_rule.static_readonly_rule.style=upper_camel_case_style dotnet_naming_rule.static_readonly_rule.symbols=static_readonly_symbols dotnet_naming_rule.types_and_namespaces_rule.severity=warning dotnet_naming_rule.types_and_namespaces_rule.style=upper_camel_case_style dotnet_naming_rule.types_and_namespaces_rule.symbols=types_and_namespaces_symbols dotnet_naming_rule.type_parameters_rule.severity=warning dotnet_naming_rule.type_parameters_rule.style=t_upper_camel_case_style dotnet_naming_rule.type_parameters_rule.symbols=type_parameters_symbols dotnet_naming_style.i_upper_camel_case_style.capitalization=pascal_case dotnet_naming_style.i_upper_camel_case_style.required_prefix=I dotnet_naming_style.lower_camel_case_style.capitalization=camel_case dotnet_naming_style.lower_camel_case_style.required_prefix=_ dotnet_naming_style.lower_camel_case_style_1.capitalization=camel_case dotnet_naming_style.t_upper_camel_case_style.capitalization=pascal_case dotnet_naming_style.t_upper_camel_case_style.required_prefix=T dotnet_naming_style.upper_camel_case_style.capitalization=pascal_case dotnet_naming_symbols.constants_symbols.applicable_accessibilities=public,internal,protected,protected_internal,private_protected dotnet_naming_symbols.constants_symbols.applicable_kinds=field dotnet_naming_symbols.constants_symbols.required_modifiers=const dotnet_naming_symbols.event_symbols.applicable_accessibilities=* dotnet_naming_symbols.event_symbols.applicable_kinds=event dotnet_naming_symbols.interfaces_symbols.applicable_accessibilities=* dotnet_naming_symbols.interfaces_symbols.applicable_kinds=interface dotnet_naming_symbols.locals_symbols.applicable_accessibilities=* dotnet_naming_symbols.locals_symbols.applicable_kinds=local dotnet_naming_symbols.local_constants_symbols.applicable_accessibilities=* dotnet_naming_symbols.local_constants_symbols.applicable_kinds=local dotnet_naming_symbols.local_constants_symbols.required_modifiers=const dotnet_naming_symbols.local_functions_symbols.applicable_accessibilities=* dotnet_naming_symbols.local_functions_symbols.applicable_kinds=local_function dotnet_naming_symbols.method_symbols.applicable_accessibilities=* dotnet_naming_symbols.method_symbols.applicable_kinds=method dotnet_naming_symbols.parameters_symbols.applicable_accessibilities=* dotnet_naming_symbols.parameters_symbols.applicable_kinds=parameter dotnet_naming_symbols.private_constants_symbols.applicable_accessibilities=private dotnet_naming_symbols.private_constants_symbols.applicable_kinds=field dotnet_naming_symbols.private_constants_symbols.required_modifiers=const dotnet_naming_symbols.private_instance_fields_symbols.applicable_accessibilities=private dotnet_naming_symbols.private_instance_fields_symbols.applicable_kinds=field dotnet_naming_symbols.private_static_fields_symbols.applicable_accessibilities=private dotnet_naming_symbols.private_static_fields_symbols.applicable_kinds=field dotnet_naming_symbols.private_static_fields_symbols.required_modifiers=static dotnet_naming_symbols.private_static_readonly_symbols.applicable_accessibilities=private dotnet_naming_symbols.private_static_readonly_symbols.applicable_kinds=field dotnet_naming_symbols.private_static_readonly_symbols.required_modifiers=static,readonly dotnet_naming_symbols.property_symbols.applicable_accessibilities=* dotnet_naming_symbols.property_symbols.applicable_kinds=property dotnet_naming_symbols.public_fields_symbols.applicable_accessibilities=public,internal,protected,protected_internal,private_protected dotnet_naming_symbols.public_fields_symbols.applicable_kinds=field dotnet_naming_symbols.static_readonly_symbols.applicable_accessibilities=public,internal,protected,protected_internal,private_protected dotnet_naming_symbols.static_readonly_symbols.applicable_kinds=field dotnet_naming_symbols.static_readonly_symbols.required_modifiers=static,readonly dotnet_naming_symbols.types_and_namespaces_symbols.applicable_accessibilities=* dotnet_naming_symbols.types_and_namespaces_symbols.applicable_kinds=namespace,class,struct,enum,delegate dotnet_naming_symbols.type_parameters_symbols.applicable_accessibilities=* dotnet_naming_symbols.type_parameters_symbols.applicable_kinds=type_parameter dotnet_style_parentheses_in_arithmetic_binary_operators=never_if_unnecessary:none dotnet_style_parentheses_in_other_binary_operators=never_if_unnecessary:none dotnet_style_parentheses_in_relational_binary_operators=never_if_unnecessary:none dotnet_style_predefined_type_for_locals_parameters_members=true:suggestion dotnet_style_predefined_type_for_member_access=true:suggestion dotnet_style_qualification_for_event=false:suggestion dotnet_style_qualification_for_field=false:suggestion dotnet_style_qualification_for_method=false:suggestion dotnet_style_qualification_for_property=false:suggestion dotnet_style_require_accessibility_modifiers=for_non_interface_members:suggestion # ReSharper properties resharper_accessor_owner_body=expression_body resharper_alignment_tab_fill_style=use_spaces resharper_align_first_arg_by_paren=false resharper_align_linq_query=false resharper_align_multiline_array_and_object_initializer=false resharper_align_multiline_array_initializer=true resharper_align_multiline_binary_expressions_chain=true resharper_align_multiline_calls_chain=false resharper_align_multiline_extends_list=false resharper_align_multiline_for_stmt=false resharper_align_multiline_implements_list=true resharper_align_multiline_switch_expression=false resharper_align_multline_type_parameter_constrains=false resharper_align_multline_type_parameter_list=false resharper_align_tuple_components=false resharper_allow_alias=true resharper_allow_comment_after_lbrace=false resharper_always_use_end_of_line_brace_style=false resharper_apply_auto_detected_rules=true resharper_apply_on_completion=false resharper_arguments_anonymous_function=positional resharper_arguments_literal=positional resharper_arguments_named=positional resharper_arguments_other=positional resharper_arguments_skip_single=false resharper_arguments_string_literal=positional resharper_attribute_style=do_not_touch resharper_autodetect_indent_settings=true resharper_blank_lines_after_block_statements=1 resharper_blank_lines_after_case=0 resharper_blank_lines_after_control_transfer_statements=0 resharper_blank_lines_after_imports=1 resharper_blank_lines_after_multiline_statements=0 resharper_blank_lines_after_options=1 resharper_blank_lines_after_start_comment=1 resharper_blank_lines_after_using_list=1 resharper_blank_lines_around_auto_property=1 resharper_blank_lines_around_block_case_section=0 resharper_blank_lines_around_field=1 resharper_blank_lines_around_global_attribute=0 resharper_blank_lines_around_invocable=1 resharper_blank_lines_around_local_method=1 resharper_blank_lines_around_multiline_case_section=0 resharper_blank_lines_around_namespace=1 resharper_blank_lines_around_property=1 resharper_blank_lines_around_razor_functions=1 resharper_blank_lines_around_razor_helpers=1 resharper_blank_lines_around_razor_sections=1 resharper_blank_lines_around_region=1 resharper_blank_lines_around_single_line_auto_property=0 resharper_blank_lines_around_single_line_field=0 resharper_blank_lines_around_single_line_invocable=0 resharper_blank_lines_around_single_line_local_method=0 resharper_blank_lines_around_single_line_property=0 resharper_blank_lines_around_type=1 resharper_blank_lines_before_block_statements=0 resharper_blank_lines_before_case=0 resharper_blank_lines_before_control_transfer_statements=0 resharper_blank_lines_before_multiline_statements=0 resharper_blank_lines_before_single_line_comment=0 resharper_blank_lines_between_using_groups=0 resharper_blank_lines_inside_namespace=0 resharper_blank_lines_inside_region=1 resharper_blank_lines_inside_type=0 resharper_blank_line_after_pi=true resharper_braces_for_dowhile=required resharper_braces_for_fixed=required resharper_braces_for_for=not_required resharper_braces_for_foreach=not_required resharper_braces_for_ifelse=not_required_for_both resharper_braces_for_lock=required resharper_braces_for_using=required resharper_braces_for_while=not_required resharper_braces_redundant=true resharper_can_use_global_alias=true resharper_constructor_or_destructor_body=block_body resharper_continuous_indent_multiplier=1 resharper_csharp_align_multiline_argument=false resharper_csharp_align_multiline_expression=false resharper_csharp_align_multiline_parameter=false resharper_csharp_align_multiple_declaration=false resharper_csharp_max_line_length=120 resharper_csharp_naming_rule.enum_member=AaBb resharper_csharp_naming_rule.method_property_event=AaBb resharper_csharp_naming_rule.other=AaBb resharper_csharp_prefer_qualified_reference=false resharper_csharp_wrap_lines=true resharper_default_exception_variable_name=e resharper_delete_quotes_from_solid_values=false resharper_disable_blank_line_changes=false resharper_disable_formatter=false resharper_disable_indenter=false resharper_disable_int_align=false resharper_disable_line_break_changes=false resharper_disable_line_break_removal=false resharper_disable_space_changes=false resharper_empty_block_style=multiline resharper_enable_wrapping=false resharper_enforce_line_ending_style=false resharper_event_handler_pattern_long=$object$On$event$ resharper_event_handler_pattern_short=On$event$ resharper_extra_spaces=remove_all resharper_force_attribute_style=separate resharper_force_chop_compound_do_expression=false resharper_force_chop_compound_if_expression=false resharper_force_chop_compound_while_expression=false resharper_format_leading_spaces_decl=false resharper_html_attribute_indent=align_by_first_attribute resharper_html_linebreak_before_elements=body,div,p,form,h1,h2,h3 resharper_html_max_blank_lines_between_tags=2 resharper_html_max_line_length=120 resharper_html_pi_attribute_style=on_single_line resharper_html_space_before_self_closing=false resharper_html_wrap_lines=true resharper_ignore_space_preservation=false resharper_include_prefix_comment_in_indent=false resharper_indent_anonymous_method_block=false resharper_indent_case_from_select=true resharper_indent_child_elements=OneIndent resharper_indent_inside_namespace=true resharper_indent_invocation_pars=inside resharper_indent_method_decl_pars=inside resharper_indent_nested_fixed_stmt=false resharper_indent_nested_foreach_stmt=false resharper_indent_nested_for_stmt=false resharper_indent_nested_lock_stmt=false resharper_indent_nested_usings_stmt=false resharper_indent_nested_while_stmt=false resharper_indent_pars=inside resharper_indent_preprocessor_if=no_indent resharper_indent_preprocessor_other=no_indent resharper_indent_preprocessor_region=usual_indent resharper_indent_statement_pars=inside resharper_indent_text=OneIndent resharper_indent_typearg_angles=inside resharper_indent_typeparam_angles=inside resharper_indent_type_constraints=true resharper_instance_members_qualify_declared_in=this_class, base_class resharper_int_align=false resharper_keep_blank_lines_in_code=2 resharper_keep_blank_lines_in_declarations=2 resharper_keep_existing_attribute_arrangement=false resharper_keep_existing_declaration_block_arrangement=false resharper_keep_existing_declaration_parens_arrangement=true resharper_keep_existing_embedded_arrangement=true resharper_keep_existing_embedded_block_arrangement=false resharper_keep_existing_enum_arrangement=false resharper_keep_existing_expr_member_arrangement=true resharper_keep_existing_invocation_parens_arrangement=true resharper_keep_existing_switch_expression_arrangement=true resharper_keep_nontrivial_alias=true resharper_keep_user_linebreaks=true resharper_keep_user_wrapping=true resharper_linebreaks_around_razor_statements=true resharper_linebreaks_inside_tags_for_elements_longer_than=2147483647 resharper_linebreaks_inside_tags_for_elements_with_child_elements=true resharper_linebreaks_inside_tags_for_multiline_elements=true resharper_linebreak_before_all_elements=false resharper_linebreak_before_multiline_elements=true resharper_linebreak_before_singleline_elements=false resharper_local_function_body=block_body resharper_max_array_initializer_elements_on_line=10000 resharper_max_attribute_length_for_same_line=38 resharper_max_enum_members_on_line=3 resharper_max_formal_parameters_on_line=10000 resharper_max_initializer_elements_on_line=4 resharper_max_invocation_arguments_on_line=10000 resharper_method_or_operator_body=block_body resharper_nested_ternary_style=autodetect resharper_new_line_before_while=false resharper_normalize_tag_names=false resharper_no_indent_inside_elements=html,body,thead,tbody,tfoot resharper_no_indent_inside_if_element_longer_than=200 resharper_old_engine=false resharper_outdent_binary_ops=false resharper_outdent_commas=false resharper_outdent_dots=false resharper_outdent_ternary_ops=false resharper_parentheses_non_obvious_operations=none, shift, bitwise_and, bitwise_exclusive_or, bitwise_inclusive_or, bitwise resharper_parentheses_redundancy_style=remove_if_not_clarifies_precedence resharper_pi_attributes_indent=align_by_first_attribute resharper_place_accessorholder_attribute_on_same_line=if_owner_is_single_line resharper_place_accessor_attribute_on_same_line=if_owner_is_single_line resharper_place_comments_at_first_column=false resharper_place_constructor_initializer_on_same_line=true resharper_place_event_attribute_on_same_line=false resharper_place_expr_accessor_on_single_line=if_owner_is_single_line resharper_place_expr_method_on_single_line=if_owner_is_single_line resharper_place_expr_property_on_single_line=if_owner_is_single_line resharper_place_field_attribute_on_same_line=true resharper_place_linq_into_on_new_line=true resharper_place_method_attribute_on_same_line=false resharper_place_property_attribute_on_same_line=false resharper_place_simple_case_statement_on_same_line=false resharper_place_simple_embedded_statement_on_same_line=if_owner_is_single_line resharper_place_simple_initializer_on_single_line=true resharper_place_simple_switch_expression_on_single_line=false resharper_place_type_attribute_on_same_line=false resharper_place_type_constraints_on_same_line=true resharper_prefer_explicit_discard_declaration=false resharper_prefer_separate_deconstructed_variables_declaration=false resharper_preserve_spaces_inside_tags=pre,textarea resharper_qualified_using_at_nested_scope=false resharper_quote_style=doublequoted resharper_razor_prefer_qualified_reference=true resharper_remove_blank_lines_near_braces=false resharper_remove_blank_lines_near_braces_in_code=true resharper_remove_blank_lines_near_braces_in_declarations=true resharper_remove_this_qualifier=true resharper_resx_attribute_indent=single_indent resharper_resx_linebreak_before_elements= resharper_resx_max_blank_lines_between_tags=0 resharper_resx_max_line_length=2147483647 resharper_resx_pi_attribute_style=do_not_touch resharper_resx_space_before_self_closing=false resharper_resx_wrap_lines=false resharper_resx_wrap_tags_and_pi=false resharper_resx_wrap_text=false resharper_show_autodetect_configure_formatting_tip=true resharper_sort_attributes=false resharper_sort_class_selectors=false resharper_sort_usings=true resharper_sort_usings_lowercase_first=false resharper_sort_usings_with_system_first=true resharper_spaces_around_eq_in_attribute=false resharper_spaces_around_eq_in_pi_attribute=false resharper_spaces_inside_tags=false resharper_space_after_attributes=true resharper_space_after_attribute_target_colon=true resharper_space_after_colon=true resharper_space_after_colon_in_case=true resharper_space_after_comma=true resharper_space_after_last_attribute=false resharper_space_after_last_pi_attribute=false resharper_space_after_operator_keyword=true resharper_space_after_triple_slash=true resharper_space_after_type_parameter_constraint_colon=true resharper_space_after_unary_operator=false resharper_space_around_additive_op=true resharper_space_around_alias_eq=true resharper_space_around_assignment_op=true resharper_space_around_lambda_arrow=true resharper_space_around_member_access_operator=false resharper_space_around_relational_op=true resharper_space_around_shift_op=true resharper_space_around_stmt_colon=true resharper_space_around_ternary_operator=true resharper_space_before_array_rank_parentheses=false resharper_space_before_attribute_target_colon=false resharper_space_before_checked_parentheses=false resharper_space_before_colon=false resharper_space_before_colon_in_case=false resharper_space_before_comma=false resharper_space_before_default_parentheses=false resharper_space_before_empty_invocation_parentheses=false resharper_space_before_empty_method_parentheses=false resharper_space_before_invocation_parentheses=false resharper_space_before_label_colon=false resharper_space_before_method_parentheses=false resharper_space_before_nameof_parentheses=false resharper_space_before_nullable_mark=false resharper_space_before_pointer_asterik_declaration=false resharper_space_before_semicolon=false resharper_space_before_singleline_accessorholder=true resharper_space_before_sizeof_parentheses=false resharper_space_before_trailing_comment=true resharper_space_before_typeof_parentheses=false resharper_space_before_type_argument_angle=false resharper_space_before_type_parameter_angle=false resharper_space_before_type_parameter_constraint_colon=true resharper_space_before_type_parameter_parentheses=true resharper_space_between_accessors_in_singleline_property=true resharper_space_between_attribute_sections=true resharper_space_between_keyword_and_expression=true resharper_space_between_keyword_and_type=true resharper_space_in_singleline_accessorholder=true resharper_space_in_singleline_anonymous_method=true resharper_space_in_singleline_method=true resharper_space_near_postfix_and_prefix_op=false resharper_space_within_array_initialization_braces=false resharper_space_within_array_rank_empty_parentheses=false resharper_space_within_array_rank_parentheses=false resharper_space_within_attribute_angles=false resharper_space_within_checked_parentheses=false resharper_space_within_default_parentheses=false resharper_space_within_empty_braces=true resharper_space_within_empty_invocation_parentheses=false resharper_space_within_empty_method_parentheses=false resharper_space_within_expression_parentheses=false resharper_space_within_invocation_parentheses=false resharper_space_within_method_parentheses=false resharper_space_within_nameof_parentheses=false resharper_space_within_single_line_array_initializer_braces=true resharper_space_within_sizeof_parentheses=false resharper_space_within_tuple_parentheses=false resharper_space_within_typeof_parentheses=false resharper_space_within_type_argument_angles=false resharper_space_within_type_parameter_angles=false resharper_space_within_type_parameter_parentheses=false resharper_special_else_if_treatment=true resharper_static_members_qualify_members=none resharper_static_members_qualify_with=do_not_change, declared_type resharper_stick_comment=true resharper_support_vs_event_naming_pattern=true resharper_trailing_comma_in_multiline_lists=false resharper_trailing_comma_in_singleline_lists=false resharper_use_continuous_indent_inside_initializer_braces=true resharper_use_continuous_indent_inside_parens=true resharper_use_heuristics_for_body_style=true resharper_use_indents_from_main_language_in_file=true resharper_use_indent_from_previous_element=true resharper_use_indent_from_vs=false resharper_use_roslyn_logic_for_evident_types=false resharper_vb_align_multiline_argument=true resharper_vb_align_multiline_expression=true resharper_vb_align_multiline_parameter=true resharper_vb_align_multiple_declaration=true resharper_vb_max_line_length=120 resharper_vb_place_field_attribute_on_same_line=true resharper_vb_place_method_attribute_on_same_line=false resharper_vb_place_type_attribute_on_same_line=false resharper_vb_prefer_qualified_reference=false resharper_vb_space_around_multiplicative_op=false resharper_vb_wrap_lines=true resharper_wrap_after_declaration_lpar=false resharper_wrap_after_dot_in_method_calls=false resharper_wrap_after_invocation_lpar=false resharper_wrap_arguments_style=wrap_if_long resharper_wrap_around_elements=true resharper_wrap_array_initializer_style=wrap_if_long resharper_wrap_before_arrow_with_expressions=false resharper_wrap_before_binary_opsign=false resharper_wrap_before_comma=false resharper_wrap_before_declaration_lpar=false resharper_wrap_before_declaration_rpar=false resharper_wrap_before_extends_colon=false resharper_wrap_before_first_type_parameter_constraint=false resharper_wrap_before_invocation_lpar=false resharper_wrap_before_invocation_rpar=false resharper_wrap_before_linq_expression=false resharper_wrap_before_ternary_opsigns=true resharper_wrap_before_type_parameter_langle=false resharper_wrap_chained_binary_expressions=wrap_if_long resharper_wrap_chained_method_calls=wrap_if_long resharper_wrap_enum_declaration=chop_always resharper_wrap_extends_list_style=wrap_if_long resharper_wrap_for_stmt_header_style=chop_if_long resharper_wrap_multiple_declaration_style=chop_if_long resharper_wrap_multiple_type_parameter_constraints_style=chop_if_long resharper_wrap_object_and_collection_initializer_style=chop_if_long resharper_wrap_parameters_style=wrap_if_long resharper_wrap_switch_expression=chop_always resharper_wrap_ternary_expr_style=chop_if_long resharper_wrap_verbatim_interpolated_strings=no_wrap resharper_xmldoc_attribute_indent=single_indent resharper_xmldoc_linebreak_before_elements=summary,remarks,example,returns,param,typeparam,value,para resharper_xmldoc_max_blank_lines_between_tags=0 resharper_xmldoc_max_line_length=120 resharper_xmldoc_pi_attribute_style=do_not_touch resharper_xmldoc_space_before_self_closing=true resharper_xmldoc_wrap_lines=true resharper_xmldoc_wrap_tags_and_pi=true resharper_xmldoc_wrap_text=true resharper_xml_attribute_indent=align_by_first_attribute resharper_xml_linebreak_before_elements= resharper_xml_max_blank_lines_between_tags=2 resharper_xml_max_line_length=120 resharper_xml_pi_attribute_style=do_not_touch resharper_xml_space_before_self_closing=true resharper_xml_wrap_lines=true resharper_xml_wrap_tags_and_pi=true resharper_xml_wrap_text=false # ReSharper inspection severities resharper_abstract_class_constructor_can_be_made_protected_highlighting=hint resharper_access_rights_in_text_highlighting=warning resharper_access_to_disposed_closure_highlighting=warning resharper_access_to_for_each_variable_in_closure_highlighting=warning resharper_access_to_modified_closure_highlighting=warning resharper_access_to_static_member_via_derived_type_highlighting=warning resharper_address_of_marshal_by_ref_object_highlighting=warning resharper_amd_dependency_path_problem_highlighting=none resharper_angular_html_banana_highlighting=warning resharper_annotate_can_be_null_parameter_highlighting=none resharper_annotate_can_be_null_type_member_highlighting=none resharper_annotate_not_null_parameter_highlighting=none resharper_annotate_not_null_type_member_highlighting=none resharper_annotation_conflict_in_hierarchy_highlighting=warning resharper_annotation_redundancy_at_value_type_highlighting=warning resharper_annotation_redundancy_in_hierarchy_highlighting=warning resharper_arguments_style_anonymous_function_highlighting=hint resharper_arguments_style_literal_highlighting=hint resharper_arguments_style_named_expression_highlighting=hint resharper_arguments_style_other_highlighting=hint resharper_arguments_style_string_literal_highlighting=hint resharper_arrange_accessor_owner_body_highlighting=suggestion resharper_arrange_attributes_highlighting=none resharper_arrange_constructor_or_destructor_body_highlighting=none resharper_arrange_local_function_body_highlighting=none resharper_arrange_method_or_operator_body_highlighting=none resharper_arrange_redundant_parentheses_highlighting=hint resharper_arrange_static_member_qualifier_highlighting=hint resharper_arrange_this_qualifier_highlighting=hint resharper_arrange_trailing_comma_in_multiline_lists_highlighting=hint resharper_arrange_trailing_comma_in_singleline_lists_highlighting=hint resharper_arrange_type_member_modifiers_highlighting=hint resharper_arrange_type_modifiers_highlighting=hint resharper_arrange_var_keywords_in_deconstructing_declaration_highlighting=suggestion resharper_asp0000_highlighting=warning resharper_asp0001_highlighting=warning resharper_asp_content_placeholder_not_resolved_highlighting=error resharper_asp_custom_page_parser_filter_type_highlighting=warning resharper_asp_dead_code_highlighting=warning resharper_asp_entity_highlighting=warning resharper_asp_image_highlighting=warning resharper_asp_invalid_control_type_highlighting=error resharper_asp_not_resolved_highlighting=error resharper_asp_ods_method_reference_resolve_error_highlighting=error resharper_asp_resolve_warning_highlighting=warning resharper_asp_skin_not_resolved_highlighting=error resharper_asp_tag_attribute_with_optional_value_highlighting=warning resharper_asp_theme_not_resolved_highlighting=error resharper_asp_unused_register_directive_highlighting_highlighting=warning resharper_asp_warning_highlighting=warning resharper_assigned_value_is_never_used_highlighting=warning resharper_assigned_value_wont_be_assigned_to_corresponding_field_highlighting=warning resharper_assignment_in_conditional_expression_highlighting=warning resharper_assignment_in_condition_expression_highlighting=warning resharper_assignment_is_fully_discarded_highlighting=warning resharper_assign_null_to_not_null_attribute_highlighting=warning resharper_assign_to_constant_highlighting=error resharper_assign_to_implicit_global_in_function_scope_highlighting=warning resharper_asxx_path_error_highlighting=warning resharper_async_iterator_invocation_without_await_foreach_highlighting=warning resharper_auto_property_can_be_made_get_only_global_highlighting=suggestion resharper_auto_property_can_be_made_get_only_local_highlighting=suggestion resharper_bad_attribute_brackets_spaces_highlighting=none resharper_bad_braces_spaces_highlighting=none resharper_bad_child_statement_indent_highlighting=warning resharper_bad_colon_spaces_highlighting=none resharper_bad_comma_spaces_highlighting=none resharper_bad_control_braces_indent_highlighting=suggestion resharper_bad_control_braces_line_breaks_highlighting=none resharper_bad_declaration_braces_indent_highlighting=none resharper_bad_declaration_braces_line_breaks_highlighting=none resharper_bad_empty_braces_line_breaks_highlighting=none resharper_bad_expression_braces_indent_highlighting=none resharper_bad_expression_braces_line_breaks_highlighting=none resharper_bad_generic_brackets_spaces_highlighting=none resharper_bad_indent_highlighting=none resharper_bad_linq_line_breaks_highlighting=none resharper_bad_list_line_breaks_highlighting=none resharper_bad_member_access_spaces_highlighting=none resharper_bad_namespace_braces_indent_highlighting=none resharper_bad_parens_line_breaks_highlighting=none resharper_bad_parens_spaces_highlighting=none resharper_bad_preprocessor_indent_highlighting=none resharper_bad_semicolon_spaces_highlighting=none resharper_bad_spaces_after_keyword_highlighting=none resharper_bad_square_brackets_spaces_highlighting=none resharper_bad_switch_braces_indent_highlighting=none resharper_bad_symbol_spaces_highlighting=none resharper_base_member_has_params_highlighting=warning resharper_base_method_call_with_default_parameter_highlighting=warning resharper_base_object_equals_is_object_equals_highlighting=warning resharper_base_object_get_hash_code_call_in_get_hash_code_highlighting=warning resharper_bitwise_operator_on_enum_without_flags_highlighting=warning resharper_bl0001_highlighting=error resharper_bl0002_highlighting=warning resharper_bl0003_highlighting=warning resharper_bl0004_highlighting=error resharper_bl0005_highlighting=warning resharper_bl0006_highlighting=warning resharper_block_scope_redeclaration_highlighting=error resharper_built_in_type_reference_style_for_member_access_highlighting=hint resharper_built_in_type_reference_style_highlighting=hint resharper_by_ref_argument_is_volatile_field_highlighting=warning resharper_caller_callee_using_error_highlighting=error resharper_caller_callee_using_highlighting=warning resharper_cannot_apply_equality_operator_to_type_highlighting=warning resharper_center_tag_is_obsolete_highlighting=warning resharper_check_for_reference_equality_instead_1_highlighting=suggestion resharper_check_for_reference_equality_instead_2_highlighting=suggestion resharper_check_for_reference_equality_instead_3_highlighting=suggestion resharper_check_for_reference_equality_instead_4_highlighting=suggestion resharper_check_namespace_highlighting=warning resharper_class_cannot_be_instantiated_highlighting=warning resharper_class_can_be_sealed_global_highlighting=none resharper_class_can_be_sealed_local_highlighting=none resharper_class_never_instantiated_global_highlighting=suggestion resharper_class_never_instantiated_local_highlighting=suggestion resharper_class_with_virtual_members_never_inherited_global_highlighting=suggestion resharper_class_with_virtual_members_never_inherited_local_highlighting=suggestion resharper_clear_attribute_is_obsolete_all_highlighting=warning resharper_clear_attribute_is_obsolete_highlighting=warning resharper_closure_on_modified_variable_highlighting=warning resharper_coerced_equals_using_highlighting=warning resharper_coerced_equals_using_with_null_undefined_highlighting=none resharper_collection_never_queried_global_highlighting=warning resharper_collection_never_queried_local_highlighting=warning resharper_collection_never_updated_global_highlighting=warning resharper_collection_never_updated_local_highlighting=warning resharper_comma_not_valid_here_highlighting=error resharper_comment_typo_highlighting=suggestion resharper_compare_non_constrained_generic_with_null_highlighting=none resharper_compare_of_floats_by_equality_operator_highlighting=warning resharper_conditional_ternary_equal_branch_highlighting=warning resharper_condition_is_always_const_highlighting=warning resharper_condition_is_always_true_or_false_highlighting=warning resharper_confusing_char_as_integer_in_constructor_highlighting=warning resharper_constant_conditional_access_qualifier_highlighting=warning resharper_constant_null_coalescing_condition_highlighting=warning resharper_constructor_call_not_used_highlighting=warning resharper_constructor_initializer_loop_highlighting=warning resharper_container_annotation_redundancy_highlighting=warning resharper_context_value_is_provided_highlighting=none resharper_contract_annotation_not_parsed_highlighting=warning resharper_convert_closure_to_method_group_highlighting=suggestion resharper_convert_conditional_ternary_expression_to_switch_expression_highlighting=hint resharper_convert_if_do_to_while_highlighting=suggestion resharper_convert_if_statement_to_conditional_ternary_expression_highlighting=suggestion resharper_convert_if_statement_to_null_coalescing_assignment_highlighting=suggestion resharper_convert_if_statement_to_null_coalescing_expression_highlighting=suggestion resharper_convert_if_statement_to_return_statement_highlighting=hint resharper_convert_if_statement_to_switch_expression_highlighting=hint resharper_convert_if_statement_to_switch_statement_highlighting=hint resharper_convert_if_to_or_expression_highlighting=suggestion resharper_convert_nullable_to_short_form_highlighting=suggestion resharper_convert_switch_statement_to_switch_expression_highlighting=hint resharper_convert_to_auto_property_highlighting=suggestion resharper_convert_to_auto_property_when_possible_highlighting=hint resharper_convert_to_auto_property_with_private_setter_highlighting=hint resharper_convert_to_compound_assignment_highlighting=hint resharper_convert_to_constant_global_highlighting=hint resharper_convert_to_constant_local_highlighting=hint resharper_convert_to_lambda_expression_highlighting=suggestion resharper_convert_to_lambda_expression_when_possible_highlighting=none resharper_convert_to_local_function_highlighting=suggestion resharper_convert_to_null_coalescing_compound_assignment_highlighting=suggestion resharper_convert_to_static_class_highlighting=suggestion resharper_convert_to_using_declaration_highlighting=suggestion resharper_convert_to_vb_auto_property_highlighting=suggestion resharper_convert_to_vb_auto_property_when_possible_highlighting=hint resharper_convert_to_vb_auto_property_with_private_setter_highlighting=hint resharper_co_variant_array_conversion_highlighting=warning resharper_create_specialized_overload_highlighting=hint resharper_css_browser_compatibility_highlighting=warning resharper_css_caniuse_feature_requires_prefix_highlighting=hint resharper_css_caniuse_unsupported_feature_highlighting=hint resharper_css_not_resolved_highlighting=error resharper_css_obsolete_highlighting=hint resharper_css_property_does_not_override_vendor_property_highlighting=warning resharper_cyclic_reference_comment_highlighting=none resharper_c_sharp_warnings_cs0078_highlighting=warning resharper_c_sharp_warnings_cs0108_cs0114_highlighting=warning resharper_c_sharp_warnings_cs0109_highlighting=warning resharper_c_sharp_warnings_cs0162_highlighting=warning resharper_c_sharp_warnings_cs0183_highlighting=warning resharper_c_sharp_warnings_cs0184_highlighting=warning resharper_c_sharp_warnings_cs0197_highlighting=warning resharper_c_sharp_warnings_cs0252_cs0253_highlighting=warning resharper_c_sharp_warnings_cs0420_highlighting=warning resharper_c_sharp_warnings_cs0465_highlighting=warning resharper_c_sharp_warnings_cs0469_highlighting=warning resharper_c_sharp_warnings_cs0612_highlighting=warning resharper_c_sharp_warnings_cs0618_highlighting=warning resharper_c_sharp_warnings_cs0628_highlighting=warning resharper_c_sharp_warnings_cs0642_highlighting=warning resharper_c_sharp_warnings_cs0657_highlighting=warning resharper_c_sharp_warnings_cs0658_highlighting=warning resharper_c_sharp_warnings_cs0659_highlighting=warning resharper_c_sharp_warnings_cs0660_cs0661_highlighting=warning resharper_c_sharp_warnings_cs0665_highlighting=warning resharper_c_sharp_warnings_cs0672_highlighting=warning resharper_c_sharp_warnings_cs0693_highlighting=warning resharper_c_sharp_warnings_cs1030_highlighting=warning resharper_c_sharp_warnings_cs1058_highlighting=warning resharper_c_sharp_warnings_cs1066_highlighting=warning resharper_c_sharp_warnings_cs1522_highlighting=warning resharper_c_sharp_warnings_cs1570_highlighting=warning resharper_c_sharp_warnings_cs1571_highlighting=warning resharper_c_sharp_warnings_cs1572_highlighting=warning resharper_c_sharp_warnings_cs1573_highlighting=warning resharper_c_sharp_warnings_cs1574_cs1584_cs1581_cs1580_highlighting=warning resharper_c_sharp_warnings_cs1574_highlighting=warning resharper_c_sharp_warnings_cs1580_highlighting=warning resharper_c_sharp_warnings_cs1584_highlighting=warning resharper_c_sharp_warnings_cs1587_highlighting=warning resharper_c_sharp_warnings_cs1589_highlighting=warning resharper_c_sharp_warnings_cs1590_highlighting=warning resharper_c_sharp_warnings_cs1591_highlighting=warning resharper_c_sharp_warnings_cs1592_highlighting=warning resharper_c_sharp_warnings_cs1710_highlighting=warning resharper_c_sharp_warnings_cs1711_highlighting=warning resharper_c_sharp_warnings_cs1712_highlighting=warning resharper_c_sharp_warnings_cs1717_highlighting=warning resharper_c_sharp_warnings_cs1723_highlighting=warning resharper_c_sharp_warnings_cs1911_highlighting=warning resharper_c_sharp_warnings_cs1957_highlighting=warning resharper_c_sharp_warnings_cs1981_highlighting=warning resharper_c_sharp_warnings_cs1998_highlighting=warning resharper_c_sharp_warnings_cs4014_highlighting=warning resharper_c_sharp_warnings_cs7095_highlighting=warning resharper_c_sharp_warnings_cs8094_highlighting=warning resharper_c_sharp_warnings_cs8123_highlighting=warning resharper_c_sharp_warnings_cs8383_highlighting=warning resharper_c_sharp_warnings_cs8416_highlighting=warning resharper_c_sharp_warnings_cs8417_highlighting=warning resharper_c_sharp_warnings_cs8425_highlighting=warning resharper_c_sharp_warnings_cs8509_highlighting=warning resharper_c_sharp_warnings_cs8597_highlighting=warning resharper_c_sharp_warnings_cs8600_highlighting=warning resharper_c_sharp_warnings_cs8601_highlighting=warning resharper_c_sharp_warnings_cs8602_highlighting=warning resharper_c_sharp_warnings_cs8603_highlighting=warning resharper_c_sharp_warnings_cs8604_highlighting=warning resharper_c_sharp_warnings_cs8605_highlighting=warning resharper_c_sharp_warnings_cs8606_highlighting=warning resharper_c_sharp_warnings_cs8608_highlighting=warning resharper_c_sharp_warnings_cs8609_highlighting=warning resharper_c_sharp_warnings_cs8610_highlighting=warning resharper_c_sharp_warnings_cs8611_highlighting=warning resharper_c_sharp_warnings_cs8612_highlighting=warning resharper_c_sharp_warnings_cs8613_highlighting=warning resharper_c_sharp_warnings_cs8614_highlighting=warning resharper_c_sharp_warnings_cs8615_highlighting=warning resharper_c_sharp_warnings_cs8616_highlighting=warning resharper_c_sharp_warnings_cs8617_highlighting=warning resharper_c_sharp_warnings_cs8618_highlighting=warning resharper_c_sharp_warnings_cs8619_highlighting=warning resharper_c_sharp_warnings_cs8620_highlighting=warning resharper_c_sharp_warnings_cs8621_highlighting=warning resharper_c_sharp_warnings_cs8622_highlighting=warning resharper_c_sharp_warnings_cs8624_highlighting=warning resharper_c_sharp_warnings_cs8625_highlighting=warning resharper_c_sharp_warnings_cs8629_highlighting=warning resharper_c_sharp_warnings_cs8631_highlighting=warning resharper_c_sharp_warnings_cs8632_highlighting=warning resharper_c_sharp_warnings_cs8633_highlighting=warning resharper_c_sharp_warnings_cs8634_highlighting=warning resharper_c_sharp_warnings_cs8643_highlighting=warning resharper_c_sharp_warnings_cs8644_highlighting=warning resharper_c_sharp_warnings_cs8645_highlighting=warning resharper_c_sharp_warnings_cs8656_highlighting=warning resharper_c_sharp_warnings_cs8667_highlighting=warning resharper_c_sharp_warnings_cs8714_highlighting=warning resharper_c_sharp_warnings_wme006_highlighting=warning resharper_declaration_hides_highlighting=hint resharper_declaration_is_empty_highlighting=warning resharper_declaration_visibility_error_highlighting=error resharper_default_value_attribute_for_optional_parameter_highlighting=warning resharper_delegate_subtraction_highlighting=warning resharper_deleting_non_qualified_reference_highlighting=error resharper_dl_tag_contains_non_dt_or_dd_elements_highlighting=hint resharper_double_colons_expected_highlighting=error resharper_double_colons_preferred_highlighting=suggestion resharper_double_negation_of_boolean_highlighting=warning resharper_double_negation_operator_highlighting=suggestion resharper_duplicate_identifier_error_highlighting=error resharper_duplicate_reference_comment_highlighting=warning resharper_duplicate_resource_highlighting=warning resharper_duplicating_local_declaration_highlighting=warning resharper_duplicating_parameter_declaration_error_highlighting=error resharper_duplicating_property_declaration_error_highlighting=error resharper_duplicating_property_declaration_highlighting=warning resharper_duplicating_switch_label_highlighting=warning resharper_dynamic_shift_right_op_is_not_int_highlighting=warning resharper_ef1001_highlighting=warning resharper_elided_trailing_element_highlighting=warning resharper_empty_constructor_highlighting=warning resharper_empty_destructor_highlighting=warning resharper_empty_embedded_statement_highlighting=warning resharper_empty_for_statement_highlighting=warning resharper_empty_general_catch_clause_highlighting=warning resharper_empty_namespace_highlighting=warning resharper_empty_object_property_declaration_highlighting=error resharper_empty_return_value_for_type_annotated_function_highlighting=warning resharper_empty_statement_highlighting=warning resharper_empty_title_tag_highlighting=hint resharper_enc0001_highlighting=info resharper_enc0002_highlighting=info resharper_enc0003_highlighting=info resharper_enc0004_highlighting=info resharper_enc0005_highlighting=info resharper_enc0006_highlighting=info resharper_enc0007_highlighting=info resharper_enc0008_highlighting=info resharper_enc0009_highlighting=info resharper_enc0010_highlighting=info resharper_enc0011_highlighting=info resharper_enc0012_highlighting=info resharper_enc0013_highlighting=info resharper_enc0014_highlighting=info resharper_enc0015_highlighting=info resharper_enc0016_highlighting=info resharper_enc0017_highlighting=info resharper_enc0018_highlighting=info resharper_enc0019_highlighting=info resharper_enc0020_highlighting=info resharper_enc0021_highlighting=info resharper_enc0023_highlighting=info resharper_enc0024_highlighting=info resharper_enc0025_highlighting=info resharper_enc0026_highlighting=info resharper_enc0028_highlighting=info resharper_enc0029_highlighting=info resharper_enc0030_highlighting=info resharper_enc0031_highlighting=info resharper_enc0032_highlighting=info resharper_enc0033_highlighting=info resharper_enc0034_highlighting=info resharper_enc0035_highlighting=info resharper_enc0036_highlighting=info resharper_enc0037_highlighting=info resharper_enc0038_highlighting=info resharper_enc0039_highlighting=info resharper_enc0040_highlighting=info resharper_enc0041_highlighting=info resharper_enc0044_highlighting=info resharper_enc0045_highlighting=info resharper_enc0046_highlighting=info resharper_enc0047_highlighting=info resharper_enc0048_highlighting=info resharper_enc0049_highlighting=info resharper_enc0050_highlighting=info resharper_enc0051_highlighting=info resharper_enc0052_highlighting=info resharper_enc0053_highlighting=info resharper_enc0054_highlighting=info resharper_enc0055_highlighting=info resharper_enc0056_highlighting=info resharper_enc0057_highlighting=info resharper_enc0058_highlighting=info resharper_enc0059_highlighting=info resharper_enc0060_highlighting=info resharper_enc0061_highlighting=info resharper_enc0062_highlighting=info resharper_enc0063_highlighting=info resharper_enc0064_highlighting=info resharper_enc0065_highlighting=info resharper_enc0066_highlighting=info resharper_enc0067_highlighting=info resharper_enc0068_highlighting=info resharper_enc0069_highlighting=info resharper_enc0070_highlighting=info resharper_enc0071_highlighting=info resharper_enc0072_highlighting=info resharper_enc0073_highlighting=info resharper_enc0074_highlighting=info resharper_enc0075_highlighting=info resharper_enc0076_highlighting=info resharper_enc0080_highlighting=info resharper_enc0081_highlighting=info resharper_enc0082_highlighting=info resharper_enc0083_highlighting=info resharper_enc0084_highlighting=info resharper_enc0085_highlighting=info resharper_enc0086_highlighting=info resharper_enc1001_highlighting=info resharper_enc1002_highlighting=info resharper_enc1003_highlighting=info resharper_enc1004_highlighting=info resharper_enforce_do_while_statement_braces_highlighting=none resharper_enforce_fixed_statement_braces_highlighting=none resharper_enforce_foreach_statement_braces_highlighting=none resharper_enforce_for_statement_braces_highlighting=none resharper_enforce_if_statement_braces_highlighting=none resharper_enforce_lock_statement_braces_highlighting=none resharper_enforce_using_statement_braces_highlighting=none resharper_enforce_while_statement_braces_highlighting=none resharper_enumerable_sum_in_explicit_unchecked_context_highlighting=warning resharper_enum_underlying_type_is_int_highlighting=warning resharper_equal_expression_comparison_highlighting=warning resharper_error_in_xml_doc_reference_highlighting=error resharper_es6_feature_highlighting=error resharper_es7_feature_highlighting=error resharper_escaped_keyword_highlighting=warning resharper_eval_arguments_name_error_highlighting=error resharper_event_never_invoked_global_highlighting=suggestion resharper_event_never_invoked_highlighting=warning resharper_event_never_subscribed_to_global_highlighting=suggestion resharper_event_never_subscribed_to_local_highlighting=suggestion resharper_event_unsubscription_via_anonymous_delegate_highlighting=warning resharper_experimental_feature_highlighting=error resharper_explicit_caller_info_argument_highlighting=warning resharper_expression_is_always_const_highlighting=warning resharper_expression_is_always_null_highlighting=warning resharper_field_can_be_made_read_only_global_highlighting=suggestion resharper_field_can_be_made_read_only_local_highlighting=suggestion resharper_foreach_can_be_converted_to_query_using_another_get_enumerator_highlighting=hint resharper_foreach_can_be_partly_converted_to_query_using_another_get_enumerator_highlighting=hint resharper_format_string_placeholders_mismatch_highlighting=warning resharper_format_string_problem_highlighting=warning resharper_for_can_be_converted_to_foreach_highlighting=suggestion resharper_for_statement_condition_is_true_highlighting=warning resharper_functions_used_before_declared_highlighting=none resharper_function_complexity_overflow_highlighting=none resharper_function_never_returns_highlighting=warning resharper_function_parameter_named_arguments_highlighting=warning resharper_function_recursive_on_all_paths_highlighting=warning resharper_function_used_out_of_scope_highlighting=warning resharper_gc_suppress_finalize_for_type_without_destructor_highlighting=warning resharper_generic_enumerator_not_disposed_highlighting=warning resharper_heuristically_unreachable_code_highlighting=warning resharper_heuristic_unreachable_code_highlighting=warning resharper_hex_color_value_with_alpha_highlighting=error resharper_html_attributes_quotes_highlighting=hint resharper_html_attribute_not_resolved_highlighting=warning resharper_html_attribute_value_not_resolved_highlighting=warning resharper_html_dead_code_highlighting=warning resharper_html_event_not_resolved_highlighting=warning resharper_html_id_duplication_highlighting=warning resharper_html_id_not_resolved_highlighting=warning resharper_html_obsolete_highlighting=warning resharper_html_path_error_highlighting=warning resharper_html_tag_not_closed_highlighting=error resharper_html_tag_not_resolved_highlighting=warning resharper_html_tag_should_be_self_closed_highlighting=warning resharper_html_tag_should_not_be_self_closed_highlighting=warning resharper_html_warning_highlighting=warning resharper_identifier_typo_highlighting=suggestion resharper_ignored_directive_highlighting=warning resharper_implicit_any_error_highlighting=error resharper_implicit_any_type_warning_highlighting=warning resharper_import_keyword_not_with_invocation_highlighting=error resharper_inactive_preprocessor_branch_highlighting=warning resharper_inconsistently_synchronized_field_highlighting=warning resharper_inconsistent_function_returns_highlighting=warning resharper_inconsistent_naming_highlighting=warning resharper_incorrect_blank_lines_near_braces_highlighting=none resharper_incorrect_operand_in_type_of_comparison_highlighting=warning resharper_incorrect_triple_slash_location_highlighting=warning resharper_indexing_by_invalid_range_highlighting=warning resharper_inheritdoc_consider_usage_highlighting=none resharper_inheritdoc_invalid_usage_highlighting=warning resharper_inline_out_variable_declaration_highlighting=suggestion resharper_internal_or_private_member_not_documented_highlighting=none resharper_interpolated_string_expression_is_not_i_formattable_highlighting=warning resharper_introduce_optional_parameters_global_highlighting=suggestion resharper_introduce_optional_parameters_local_highlighting=suggestion resharper_introduce_variable_to_apply_guard_highlighting=hint resharper_int_division_by_zero_highlighting=warning resharper_int_relational_or_equality_expression_always_same_value_highlighting=warning resharper_int_variable_overflow_highlighting=warning resharper_int_variable_overflow_in_checked_context_highlighting=warning resharper_int_variable_overflow_in_unchecked_context_highlighting=warning resharper_invalid_attribute_value_highlighting=warning resharper_invalid_json_syntax_highlighting=error resharper_invalid_task_element_highlighting=none resharper_invalid_value_highlighting=error resharper_invalid_value_type_highlighting=warning resharper_invalid_xml_doc_comment_highlighting=warning resharper_invert_condition_1_highlighting=hint resharper_invert_if_highlighting=hint resharper_invocation_is_skipped_highlighting=hint resharper_invocation_of_non_function_highlighting=warning resharper_invoked_expression_maybe_non_function_highlighting=warning resharper_invoke_as_extension_method_highlighting=suggestion resharper_is_expression_always_false_highlighting=warning resharper_is_expression_always_of_type_highlighting=warning resharper_is_expression_always_true_highlighting=warning resharper_iterator_method_result_is_ignored_highlighting=warning resharper_iterator_never_returns_highlighting=warning resharper_join_declaration_and_initializer_highlighting=suggestion resharper_join_declaration_and_initializer_js_highlighting=suggestion resharper_join_null_check_with_usage_highlighting=suggestion resharper_join_null_check_with_usage_when_possible_highlighting=none resharper_json_validation_failed_highlighting=error resharper_js_path_not_found_highlighting=error resharper_js_unreachable_code_highlighting=warning resharper_jump_must_be_in_loop_highlighting=warning resharper_label_or_semicolon_expected_highlighting=error resharper_less_specific_overload_than_main_signature_highlighting=warning resharper_lexical_declaration_needs_block_highlighting=error resharper_localizable_element_highlighting=warning resharper_local_function_can_be_made_static_highlighting=hint resharper_local_function_redefined_later_highlighting=warning resharper_local_name_captured_only_highlighting=warning resharper_local_variable_hides_member_highlighting=warning resharper_long_literal_ending_lower_l_highlighting=warning resharper_loop_can_be_converted_to_query_highlighting=hint resharper_loop_can_be_partly_converted_to_query_highlighting=none resharper_loop_variable_is_never_changed_inside_loop_highlighting=warning resharper_l_value_is_expected_highlighting=error resharper_markup_attribute_typo_highlighting=suggestion resharper_markup_text_typo_highlighting=suggestion resharper_meaningless_default_parameter_value_highlighting=warning resharper_member_can_be_internal_highlighting=none resharper_member_can_be_made_static_global_highlighting=hint resharper_member_can_be_made_static_local_highlighting=hint resharper_member_can_be_private_global_highlighting=suggestion resharper_member_can_be_private_local_highlighting=suggestion resharper_member_can_be_protected_global_highlighting=suggestion resharper_member_can_be_protected_local_highlighting=suggestion resharper_member_hides_static_from_outer_class_highlighting=warning resharper_member_initializer_value_ignored_highlighting=warning resharper_merge_cast_with_type_check_highlighting=suggestion resharper_merge_conditional_expression_highlighting=suggestion resharper_merge_conditional_expression_when_possible_highlighting=none resharper_merge_sequential_checks_highlighting=suggestion resharper_merge_sequential_checks_when_possible_highlighting=none resharper_method_has_async_overload_highlighting=suggestion resharper_method_has_async_overload_with_cancellation_highlighting=suggestion resharper_method_overload_with_optional_parameter_highlighting=warning resharper_method_supports_cancellation_highlighting=suggestion resharper_missing_alt_attribute_in_img_tag_highlighting=hint resharper_missing_attribute_highlighting=warning resharper_missing_blank_lines_highlighting=none resharper_missing_body_tag_highlighting=warning resharper_missing_has_own_property_in_foreach_highlighting=warning resharper_missing_head_and_body_tags_highlighting=warning resharper_missing_head_tag_highlighting=warning resharper_missing_indent_highlighting=none resharper_missing_linebreak_highlighting=none resharper_missing_space_highlighting=none resharper_missing_title_tag_highlighting=hint resharper_misuse_of_owner_function_this_highlighting=warning resharper_more_specific_foreach_variable_type_available_highlighting=suggestion resharper_more_specific_signature_after_less_specific_highlighting=warning resharper_multiple_declarations_in_foreach_highlighting=error resharper_multiple_nullable_attributes_usage_highlighting=warning resharper_multiple_order_by_highlighting=warning resharper_multiple_output_tags_highlighting=warning resharper_multiple_resolve_candidates_in_text_highlighting=warning resharper_multiple_spaces_highlighting=none resharper_multiple_statements_on_one_line_highlighting=none resharper_multiple_type_members_on_one_line_highlighting=none resharper_must_use_return_value_highlighting=warning resharper_mvc1000_highlighting=warning resharper_mvc1001_highlighting=warning resharper_mvc1002_highlighting=warning resharper_mvc1003_highlighting=warning resharper_mvc1004_highlighting=warning resharper_mvc1005_highlighting=warning resharper_mvc1006_highlighting=error resharper_mvc_action_not_resolved_highlighting=error resharper_mvc_area_not_resolved_highlighting=error resharper_mvc_controller_not_resolved_highlighting=error resharper_mvc_invalid_model_type_highlighting=error resharper_mvc_masterpage_not_resolved_highlighting=error resharper_mvc_partial_view_not_resolved_highlighting=error resharper_mvc_template_not_resolved_highlighting=error resharper_mvc_view_component_not_resolved_highlighting=error resharper_mvc_view_component_view_not_resolved_highlighting=error resharper_mvc_view_not_resolved_highlighting=error resharper_native_type_prototype_extending_highlighting=warning resharper_native_type_prototype_overwriting_highlighting=warning resharper_negative_equality_expression_highlighting=suggestion resharper_negative_index_highlighting=warning resharper_nested_string_interpolation_highlighting=suggestion resharper_non_assigned_constant_highlighting=error resharper_non_constant_equality_expression_has_constant_result_highlighting=warning resharper_non_readonly_member_in_get_hash_code_highlighting=warning resharper_non_volatile_field_in_double_check_locking_highlighting=warning resharper_not_accessed_field_compiler_highlighting=warning resharper_not_accessed_field_global_highlighting=suggestion resharper_not_accessed_field_local_highlighting=warning resharper_not_accessed_variable_compiler_highlighting=warning resharper_not_accessed_variable_highlighting=warning resharper_not_all_paths_return_value_highlighting=warning resharper_not_assigned_out_parameter_highlighting=warning resharper_not_declared_in_parent_culture_highlighting=warning resharper_not_null_member_is_not_initialized_highlighting=warning resharper_not_observable_annotation_redundancy_highlighting=warning resharper_not_overridden_in_specific_culture_highlighting=warning resharper_not_resolved_highlighting=warning resharper_not_resolved_in_text_highlighting=warning resharper_no_support_for_vb_highlighting=warning resharper_n_unit_async_method_must_be_task_highlighting=warning resharper_n_unit_incorrect_argument_type_highlighting=warning resharper_n_unit_incorrect_expected_result_type_highlighting=warning resharper_n_unit_method_with_parameters_and_test_attribute_highlighting=warning resharper_n_unit_missing_arguments_in_test_case_attribute_highlighting=warning resharper_n_unit_non_public_method_with_test_attribute_highlighting=warning resharper_n_unit_redundant_argument_instead_of_expected_result_highlighting=warning resharper_n_unit_redundant_argument_in_test_case_attribute_highlighting=warning resharper_n_unit_redundant_expected_result_in_test_case_attribute_highlighting=warning resharper_n_unit_test_case_attribute_requires_expected_result_highlighting=warning resharper_n_unit_test_case_result_property_duplicates_expected_result_highlighting=warning resharper_n_unit_test_case_result_property_is_obsolete_highlighting=warning resharper_n_unit_test_case_source_cannot_be_resolved_highlighting=warning resharper_n_unit_test_case_source_must_be_field_property_method_highlighting=warning resharper_n_unit_test_case_source_must_be_static_highlighting=warning resharper_n_unit_test_case_source_should_implement_i_enumerable_highlighting=warning resharper_object_creation_as_statement_highlighting=warning resharper_object_destructuring_without_parentheses_highlighting=error resharper_object_literals_are_not_comma_free_highlighting=error resharper_obsolete_element_error_highlighting=error resharper_obsolete_element_highlighting=warning resharper_octal_literals_not_allowed_error_highlighting=error resharper_ol_tag_contains_non_li_elements_highlighting=hint resharper_one_way_operation_contract_with_return_type_highlighting=warning resharper_operation_contract_without_service_contract_highlighting=warning resharper_operator_is_can_be_used_highlighting=warning resharper_optional_parameter_hierarchy_mismatch_highlighting=warning resharper_optional_parameter_ref_out_highlighting=warning resharper_other_tags_inside_script1_highlighting=error resharper_other_tags_inside_script2_highlighting=error resharper_other_tags_inside_unclosed_script_highlighting=error resharper_outdent_is_off_prev_level_highlighting=none resharper_output_tag_required_highlighting=warning resharper_overridden_with_empty_value_highlighting=warning resharper_overridden_with_same_value_highlighting=suggestion resharper_parameter_doesnt_make_any_sense_highlighting=warning resharper_parameter_hides_member_highlighting=warning resharper_parameter_only_used_for_precondition_check_global_highlighting=suggestion resharper_parameter_only_used_for_precondition_check_local_highlighting=warning resharper_parameter_type_can_be_enumerable_global_highlighting=hint resharper_parameter_type_can_be_enumerable_local_highlighting=hint resharper_parameter_value_is_not_used_highlighting=warning resharper_partial_method_parameter_name_mismatch_highlighting=warning resharper_partial_method_with_single_part_highlighting=warning resharper_partial_type_with_single_part_highlighting=warning resharper_path_not_resolved_highlighting=error resharper_pattern_always_matches_highlighting=warning resharper_pattern_always_of_type_highlighting=warning resharper_pattern_never_matches_highlighting=warning resharper_polymorphic_field_like_event_invocation_highlighting=warning resharper_possible_infinite_inheritance_highlighting=warning resharper_possible_intended_rethrow_highlighting=warning resharper_possible_interface_member_ambiguity_highlighting=warning resharper_possible_invalid_cast_exception_highlighting=warning resharper_possible_invalid_cast_exception_in_foreach_loop_highlighting=warning resharper_possible_invalid_operation_exception_highlighting=warning resharper_possible_loss_of_fraction_highlighting=warning resharper_possible_mistaken_argument_highlighting=warning resharper_possible_mistaken_call_to_get_type_1_highlighting=warning resharper_possible_mistaken_call_to_get_type_2_highlighting=warning resharper_possible_multiple_enumeration_highlighting=warning resharper_possible_multiple_write_access_in_double_check_locking_highlighting=warning resharper_possible_null_reference_exception_highlighting=warning resharper_possible_struct_member_modification_of_non_variable_struct_highlighting=warning resharper_possible_unintended_linear_search_in_set_highlighting=warning resharper_possible_unintended_queryable_as_enumerable_highlighting=suggestion resharper_possible_unintended_reference_comparison_highlighting=warning resharper_possible_write_to_me_highlighting=warning resharper_possibly_impure_method_call_on_readonly_variable_highlighting=warning resharper_possibly_incorrectly_broken_statement_highlighting=warning resharper_possibly_missing_indexer_initializer_comma_highlighting=warning resharper_possibly_mistaken_use_of_interpolated_string_insert_highlighting=warning resharper_possibly_mistaken_use_of_params_method_highlighting=warning resharper_possibly_unassigned_property_highlighting=hint resharper_private_field_can_be_converted_to_local_variable_highlighting=warning resharper_private_variable_can_be_made_readonly_highlighting=hint resharper_property_getter_cannot_have_parameters_highlighting=error resharper_property_not_resolved_highlighting=error resharper_property_setter_must_have_single_parameter_highlighting=error resharper_public_constructor_in_abstract_class_highlighting=suggestion resharper_pure_attribute_on_void_method_highlighting=warning resharper_qualified_expression_is_null_highlighting=warning resharper_qualified_expression_maybe_null_highlighting=warning resharper_razor_layout_not_resolved_highlighting=error resharper_razor_section_not_resolved_highlighting=error resharper_read_access_in_double_check_locking_highlighting=warning resharper_redundant_abstract_modifier_highlighting=warning resharper_redundant_anonymous_type_property_name_highlighting=warning resharper_redundant_argument_default_value_highlighting=warning resharper_redundant_array_creation_expression_highlighting=hint resharper_redundant_array_lower_bound_specification_highlighting=warning resharper_redundant_assignment_highlighting=warning resharper_redundant_attribute_parentheses_highlighting=hint resharper_redundant_attribute_usage_property_highlighting=suggestion resharper_redundant_base_constructor_call_highlighting=warning resharper_redundant_base_qualifier_highlighting=warning resharper_redundant_blank_lines_highlighting=none resharper_redundant_block_highlighting=warning resharper_redundant_bool_compare_highlighting=warning resharper_redundant_case_label_highlighting=warning resharper_redundant_cast_0_highlighting=warning resharper_redundant_cast_highlighting=warning resharper_redundant_catch_clause_highlighting=warning resharper_redundant_check_before_assignment_highlighting=warning resharper_redundant_collection_initializer_element_braces_highlighting=hint resharper_redundant_comparison_with_boolean_highlighting=warning resharper_redundant_css_hack_highlighting=warning resharper_redundant_declaration_semicolon_highlighting=hint resharper_redundant_default_member_initializer_highlighting=warning resharper_redundant_delegate_creation_highlighting=warning resharper_redundant_disable_warning_comment_highlighting=warning resharper_redundant_discarded_pattern_highlighting=suggestion resharper_redundant_discard_designation_highlighting=suggestion resharper_redundant_else_block_highlighting=warning resharper_redundant_empty_case_else_highlighting=warning resharper_redundant_empty_constructor_highlighting=warning resharper_redundant_empty_finally_block_highlighting=warning resharper_redundant_empty_object_creation_argument_list_highlighting=hint resharper_redundant_empty_object_or_collection_initializer_highlighting=warning resharper_redundant_empty_switch_section_highlighting=warning resharper_redundant_enumerable_cast_call_highlighting=warning resharper_redundant_explicit_array_creation_highlighting=warning resharper_redundant_explicit_array_size_highlighting=warning resharper_redundant_explicit_nullable_creation_highlighting=warning resharper_redundant_explicit_params_array_creation_highlighting=suggestion resharper_redundant_explicit_tuple_component_name_highlighting=warning resharper_redundant_extends_list_entry_highlighting=warning resharper_redundant_fixed_pointer_declaration_highlighting=suggestion resharper_redundant_highlighting=warning resharper_redundant_if_else_block_highlighting=hint resharper_redundant_if_statement_then_keyword_highlighting=none resharper_redundant_immediate_delegate_invocation_highlighting=suggestion resharper_redundant_include_highlighting=warning resharper_redundant_intermediate_variable_highlighting=hint resharper_redundant_iterator_keyword_highlighting=warning resharper_redundant_jump_statement_highlighting=warning resharper_redundant_lambda_parameter_type_highlighting=warning resharper_redundant_lambda_signature_parentheses_highlighting=hint resharper_redundant_linebreak_highlighting=none resharper_redundant_local_class_name_highlighting=hint resharper_redundant_local_function_name_highlighting=hint resharper_redundant_logical_conditional_expression_operand_highlighting=warning resharper_redundant_me_qualifier_highlighting=warning resharper_redundant_my_base_qualifier_highlighting=warning resharper_redundant_my_class_qualifier_highlighting=warning resharper_redundant_name_qualifier_highlighting=warning resharper_redundant_not_null_constraint_highlighting=warning resharper_redundant_nullable_annotation_on_reference_type_constraint_highlighting=warning resharper_redundant_nullable_annotation_on_type_constraint_has_non_nullable_base_type_highlighting=warning resharper_redundant_nullable_annotation_on_type_constraint_has_non_nullable_type_kind_highlighting=warning resharper_redundant_nullable_type_mark_highlighting=warning resharper_redundant_overflow_checking_context_highlighting=warning resharper_redundant_overload_global_highlighting=suggestion resharper_redundant_overload_local_highlighting=suggestion resharper_redundant_overridden_member_highlighting=warning resharper_redundant_params_highlighting=warning resharper_redundant_parentheses_highlighting=none resharper_redundant_parent_type_declaration_highlighting=warning resharper_redundant_property_parentheses_highlighting=hint resharper_redundant_property_pattern_clause_highlighting=suggestion resharper_redundant_qualifier_highlighting=warning resharper_redundant_query_order_by_ascending_keyword_highlighting=hint resharper_redundant_range_bound_highlighting=suggestion resharper_redundant_readonly_modifier_highlighting=suggestion resharper_redundant_setter_value_parameter_declaration_highlighting=hint resharper_redundant_space_highlighting=none resharper_redundant_string_format_call_highlighting=warning resharper_redundant_string_interpolation_highlighting=suggestion resharper_redundant_string_to_char_array_call_highlighting=warning resharper_redundant_string_type_highlighting=suggestion resharper_redundant_ternary_expression_highlighting=warning resharper_redundant_to_string_call_for_value_type_highlighting=hint resharper_redundant_to_string_call_highlighting=warning resharper_redundant_type_arguments_of_method_highlighting=warning resharper_redundant_type_cast_highlighting=warning resharper_redundant_type_cast_structural_highlighting=warning resharper_redundant_type_specification_in_default_expression_highlighting=suggestion resharper_redundant_units_highlighting=warning resharper_redundant_unsafe_context_highlighting=warning resharper_redundant_using_directive_highlighting=warning resharper_redundant_variable_type_specification_highlighting=hint resharper_redundant_verbatim_prefix_highlighting=suggestion resharper_redundant_verbatim_string_prefix_highlighting=suggestion resharper_reference_equals_with_value_type_highlighting=warning resharper_reg_exp_inspections_highlighting=warning resharper_remove_constructor_invocation_highlighting=none resharper_remove_redundant_braces_highlighting=none resharper_remove_redundant_or_statement_false_highlighting=suggestion resharper_remove_redundant_or_statement_true_highlighting=suggestion resharper_remove_to_list_1_highlighting=suggestion resharper_remove_to_list_2_highlighting=suggestion resharper_replace_indicing_with_array_destructuring_highlighting=hint resharper_replace_indicing_with_short_hand_properties_after_destructuring_highlighting=hint resharper_replace_undefined_checking_series_with_object_destructuring_highlighting=hint resharper_replace_with_destructuring_swap_highlighting=hint resharper_replace_with_first_or_default_1_highlighting=suggestion resharper_replace_with_first_or_default_2_highlighting=suggestion resharper_replace_with_first_or_default_3_highlighting=suggestion resharper_replace_with_first_or_default_4_highlighting=suggestion resharper_replace_with_last_or_default_1_highlighting=suggestion resharper_replace_with_last_or_default_2_highlighting=suggestion resharper_replace_with_last_or_default_3_highlighting=suggestion resharper_replace_with_last_or_default_4_highlighting=suggestion resharper_replace_with_of_type_1_highlighting=suggestion resharper_replace_with_of_type_2_highlighting=suggestion resharper_replace_with_of_type_3_highlighting=suggestion resharper_replace_with_of_type_any_1_highlighting=suggestion resharper_replace_with_of_type_any_2_highlighting=suggestion resharper_replace_with_of_type_count_1_highlighting=suggestion resharper_replace_with_of_type_count_2_highlighting=suggestion resharper_replace_with_of_type_first_1_highlighting=suggestion resharper_replace_with_of_type_first_2_highlighting=suggestion resharper_replace_with_of_type_first_or_default_1_highlighting=suggestion resharper_replace_with_of_type_first_or_default_2_highlighting=suggestion resharper_replace_with_of_type_last_1_highlighting=suggestion resharper_replace_with_of_type_last_2_highlighting=suggestion resharper_replace_with_of_type_last_or_default_1_highlighting=suggestion resharper_replace_with_of_type_last_or_default_2_highlighting=suggestion resharper_replace_with_of_type_long_count_highlighting=suggestion resharper_replace_with_of_type_single_1_highlighting=suggestion resharper_replace_with_of_type_single_2_highlighting=suggestion resharper_replace_with_of_type_single_or_default_1_highlighting=suggestion resharper_replace_with_of_type_single_or_default_2_highlighting=suggestion resharper_replace_with_of_type_where_highlighting=suggestion resharper_replace_with_simple_assignment_false_highlighting=suggestion resharper_replace_with_simple_assignment_true_highlighting=suggestion resharper_replace_with_single_assignment_false_highlighting=suggestion resharper_replace_with_single_assignment_true_highlighting=suggestion resharper_replace_with_single_call_to_any_highlighting=suggestion resharper_replace_with_single_call_to_count_highlighting=suggestion resharper_replace_with_single_call_to_first_highlighting=suggestion resharper_replace_with_single_call_to_first_or_default_highlighting=suggestion resharper_replace_with_single_call_to_last_highlighting=suggestion resharper_replace_with_single_call_to_last_or_default_highlighting=suggestion resharper_replace_with_single_call_to_single_highlighting=suggestion resharper_replace_with_single_call_to_single_or_default_highlighting=suggestion resharper_replace_with_single_or_default_1_highlighting=suggestion resharper_replace_with_single_or_default_2_highlighting=suggestion resharper_replace_with_single_or_default_3_highlighting=suggestion resharper_replace_with_single_or_default_4_highlighting=suggestion resharper_replace_with_string_is_null_or_empty_highlighting=suggestion resharper_required_base_types_conflict_highlighting=warning resharper_required_base_types_direct_conflict_highlighting=warning resharper_required_base_types_is_not_inherited_highlighting=warning resharper_requires_fallback_color_highlighting=warning resharper_resource_item_not_resolved_highlighting=error resharper_resource_not_resolved_highlighting=error resharper_resx_not_resolved_highlighting=warning resharper_return_from_global_scopet_with_value_highlighting=warning resharper_return_type_can_be_enumerable_global_highlighting=hint resharper_return_type_can_be_enumerable_local_highlighting=hint resharper_return_value_of_pure_method_is_not_used_highlighting=warning resharper_safe_cast_is_used_as_type_check_highlighting=suggestion resharper_same_imports_with_different_name_highlighting=warning resharper_same_variable_assignment_highlighting=warning resharper_script_tag_has_both_src_and_content_attributes_highlighting=error resharper_script_tag_with_content_before_includes_highlighting=hint resharper_sealed_member_in_sealed_class_highlighting=warning resharper_sensitive_data_api_usage_tag_highlighting=warning resharper_separate_control_transfer_statement_highlighting=none resharper_service_contract_without_operations_highlighting=warning resharper_shift_expression_real_shift_count_is_zero_highlighting=warning resharper_shift_expression_result_equals_zero_highlighting=warning resharper_shift_expression_right_operand_not_equal_real_count_highlighting=warning resharper_shift_expression_zero_left_operand_highlighting=warning resharper_similar_anonymous_type_nearby_highlighting=hint resharper_similar_expressions_comparison_highlighting=warning resharper_simplify_conditional_operator_highlighting=suggestion resharper_simplify_conditional_ternary_expression_highlighting=suggestion resharper_simplify_i_if_highlighting=suggestion resharper_simplify_linq_expression_highlighting=suggestion resharper_specify_a_culture_in_string_conversion_explicitly_highlighting=warning resharper_specify_string_comparison_highlighting=hint resharper_specify_variable_type_explicitly_highlighting=hint resharper_stack_alloc_inside_loop_highlighting=warning resharper_statement_termination_highlighting=warning resharper_static_member_initializer_referes_to_member_below_highlighting=warning resharper_static_member_in_generic_type_highlighting=warning resharper_static_problem_in_text_highlighting=warning resharper_string_compare_is_culture_specific_1_highlighting=warning resharper_string_compare_is_culture_specific_2_highlighting=warning resharper_string_compare_is_culture_specific_3_highlighting=warning resharper_string_compare_is_culture_specific_4_highlighting=warning resharper_string_compare_is_culture_specific_5_highlighting=warning resharper_string_compare_is_culture_specific_6_highlighting=warning resharper_string_compare_to_is_culture_specific_highlighting=warning resharper_string_concatenation_to_template_string_highlighting=hint resharper_string_ends_with_is_culture_specific_highlighting=none resharper_string_index_of_is_culture_specific_1_highlighting=warning resharper_string_index_of_is_culture_specific_2_highlighting=warning resharper_string_index_of_is_culture_specific_3_highlighting=warning resharper_string_last_index_of_is_culture_specific_1_highlighting=warning resharper_string_last_index_of_is_culture_specific_2_highlighting=warning resharper_string_last_index_of_is_culture_specific_3_highlighting=warning resharper_string_literal_as_interpolation_argument_highlighting=suggestion resharper_string_literal_typo_highlighting=suggestion resharper_string_literal_wrong_quotes_highlighting=hint resharper_string_starts_with_is_culture_specific_highlighting=none resharper_struct_can_be_made_read_only_highlighting=suggestion resharper_struct_member_can_be_made_read_only_highlighting=none resharper_suggest_base_type_for_parameter_highlighting=hint resharper_suggest_discard_declaration_var_style_highlighting=hint resharper_suggest_var_or_type_built_in_types_highlighting=hint resharper_suggest_var_or_type_deconstruction_declarations_highlighting=hint resharper_suggest_var_or_type_elsewhere_highlighting=hint resharper_suggest_var_or_type_simple_types_highlighting=hint resharper_super_call_prohibits_this_highlighting=error resharper_suspicious_instanceof_check_highlighting=warning resharper_suspicious_lambda_block_highlighting=warning resharper_suspicious_this_usage_highlighting=warning resharper_suspicious_typeof_check_highlighting=warning resharper_suspicious_type_conversion_global_highlighting=warning resharper_switch_expression_handles_some_known_enum_values_with_exception_in_default_highlighting=hint resharper_switch_statement_for_enum_misses_default_section_highlighting=hint resharper_switch_statement_handles_some_known_enum_values_with_default_highlighting=hint resharper_switch_statement_missing_some_enum_cases_no_default_highlighting=hint resharper_symbol_from_not_copied_locally_reference_used_warning_highlighting=warning resharper_syntax_is_not_allowed_highlighting=warning resharper_tabs_and_spaces_mismatch_highlighting=none resharper_tabs_are_disallowed_highlighting=none resharper_tabs_outside_indent_highlighting=none resharper_tail_recursive_call_highlighting=hint resharper_tasks_not_loaded_highlighting=warning resharper_ternary_can_be_replaced_by_its_condition_highlighting=warning resharper_this_in_global_context_highlighting=warning resharper_thread_static_at_instance_field_highlighting=warning resharper_thread_static_field_has_initializer_highlighting=warning resharper_throw_must_be_followed_by_expression_highlighting=error resharper_too_wide_local_variable_scope_highlighting=suggestion resharper_tree_node_enumerable_can_be_used_tag_highlighting=none resharper_try_cast_always_succeeds_highlighting=suggestion resharper_try_statements_can_be_merged_highlighting=hint resharper_ts_not_resolved_highlighting=error resharper_ts_resolved_from_inaccessible_module_highlighting=error resharper_type_guard_doesnt_affect_anything_highlighting=warning resharper_type_guard_produces_never_type_highlighting=warning resharper_type_parameter_can_be_variant_highlighting=suggestion resharper_type_parameter_hides_type_param_from_outer_scope_highlighting=warning resharper_ul_tag_contains_non_li_elements_highlighting=hint resharper_unassigned_field_compiler_highlighting=warning resharper_unassigned_field_global_highlighting=suggestion resharper_unassigned_field_local_highlighting=warning resharper_unassigned_get_only_auto_property_highlighting=warning resharper_unassigned_readonly_field_compiler_highlighting=warning resharper_unassigned_readonly_field_highlighting=warning resharper_unclosed_script_highlighting=error resharper_undeclared_global_variable_using_highlighting=warning resharper_unexpected_attribute_highlighting=warning resharper_unexpected_directive_highlighting=warning resharper_unexpected_value_highlighting=error resharper_unknown_css_class_highlighting=warning resharper_unknown_css_variable_highlighting=warning resharper_unknown_css_vendor_extension_highlighting=hint resharper_unknown_item_group_highlighting=warning resharper_unknown_metadata_highlighting=warning resharper_unknown_output_parameter_highlighting=warning resharper_unknown_property_highlighting=warning resharper_unknown_target_highlighting=warning resharper_unknown_task_attribute_highlighting=warning resharper_unknown_task_highlighting=warning resharper_unnecessary_whitespace_highlighting=none resharper_unreachable_code_highlighting=warning resharper_unreachable_switch_arm_due_to_integer_analysis_highlighting=warning resharper_unreachable_switch_case_due_to_integer_analysis_highlighting=warning resharper_unresolved_assembly_highlighting=warning resharper_unresolved_include_highlighting=warning resharper_unsafe_comma_in_object_properties_list_highlighting=warning resharper_unsupported_required_base_type_highlighting=warning resharper_unused_anonymous_method_signature_highlighting=warning resharper_unused_auto_property_accessor_global_highlighting=warning resharper_unused_auto_property_accessor_local_highlighting=warning resharper_unused_field_compiler_highlighting=warning resharper_unused_import_clause_highlighting=warning resharper_unused_inherited_parameter_highlighting=hint resharper_unused_label_highlighting=warning resharper_unused_locals_highlighting=warning resharper_unused_local_function_compiler_highlighting=warning resharper_unused_local_function_highlighting=warning resharper_unused_local_function_parameter_highlighting=warning resharper_unused_local_function_return_value_highlighting=warning resharper_unused_local_import_highlighting=warning resharper_unused_member_global_highlighting=suggestion resharper_unused_member_hierarchy_global_highlighting=suggestion resharper_unused_member_hierarchy_local_highlighting=warning resharper_unused_member_in_super_global_highlighting=suggestion resharper_unused_member_in_super_local_highlighting=warning resharper_unused_member_local_highlighting=warning resharper_unused_method_return_value_global_highlighting=suggestion resharper_unused_method_return_value_local_highlighting=warning resharper_unused_parameter_global_highlighting=suggestion resharper_unused_parameter_highlighting=warning resharper_unused_parameter_in_partial_method_highlighting=warning resharper_unused_parameter_local_highlighting=warning resharper_unused_property_highlighting=warning resharper_unused_tuple_component_in_return_value_highlighting=warning resharper_unused_type_global_highlighting=suggestion resharper_unused_type_local_highlighting=warning resharper_unused_type_parameter_highlighting=warning resharper_unused_variable_compiler_highlighting=warning resharper_unused_variable_highlighting=warning resharper_usage_of_definitely_unassigned_value_highlighting=warning resharper_usage_of_possibly_unassigned_value_highlighting=warning resharper_useless_binary_operation_highlighting=warning resharper_use_array_creation_expression_1_highlighting=suggestion resharper_use_array_creation_expression_2_highlighting=suggestion resharper_use_as_instead_of_type_cast_highlighting=hint resharper_use_await_using_highlighting=suggestion resharper_use_cancellation_token_for_i_async_enumerable_highlighting=suggestion resharper_use_collection_count_property_highlighting=suggestion resharper_use_deconstruction_highlighting=hint resharper_use_deconstruction_on_parameter_highlighting=hint resharper_use_format_specifier_in_format_string_highlighting=suggestion resharper_use_format_specifier_in_interpolation_highlighting=suggestion resharper_use_implicitly_typed_variable_evident_highlighting=hint resharper_use_implicitly_typed_variable_highlighting=none resharper_use_implicit_by_val_modifier_highlighting=hint resharper_use_indexed_property_highlighting=suggestion resharper_use_index_from_end_expression_highlighting=suggestion resharper_use_is_operator_1_highlighting=suggestion resharper_use_is_operator_2_highlighting=suggestion resharper_use_method_any_0_highlighting=suggestion resharper_use_method_any_1_highlighting=suggestion resharper_use_method_any_2_highlighting=suggestion resharper_use_method_any_3_highlighting=suggestion resharper_use_method_any_4_highlighting=suggestion resharper_use_method_is_instance_of_type_highlighting=suggestion resharper_use_nameof_expression_highlighting=suggestion resharper_use_name_of_instead_of_type_of_highlighting=suggestion resharper_use_negated_pattern_matching_highlighting=hint resharper_use_null_propagation_highlighting=suggestion resharper_use_null_propagation_when_possible_highlighting=none resharper_use_object_or_collection_initializer_highlighting=suggestion resharper_use_of_implicit_global_in_function_scope_highlighting=warning resharper_use_of_possibly_unassigned_property_highlighting=warning resharper_use_pattern_matching_highlighting=suggestion resharper_use_string_interpolation_highlighting=suggestion resharper_use_switch_case_pattern_variable_highlighting=suggestion resharper_use_verbatim_string_highlighting=hint resharper_using_of_reserved_word_error_highlighting=error resharper_using_of_reserved_word_highlighting=warning resharper_value_parameter_not_used_highlighting=warning resharper_value_should_have_units_highlighting=error resharper_variable_can_be_made_const_highlighting=hint resharper_variable_can_be_made_let_highlighting=hint resharper_variable_can_be_moved_to_inner_block_highlighting=hint resharper_variable_hides_outer_variable_highlighting=warning resharper_variable_used_before_declared_highlighting=warning resharper_variable_used_in_inner_scope_before_declared_highlighting=warning resharper_variable_used_out_of_scope_highlighting=warning resharper_vb_check_for_reference_equality_instead_1_highlighting=suggestion resharper_vb_check_for_reference_equality_instead_2_highlighting=suggestion resharper_vb_possible_mistaken_argument_highlighting=warning resharper_vb_possible_mistaken_call_to_get_type_1_highlighting=warning resharper_vb_possible_mistaken_call_to_get_type_2_highlighting=warning resharper_vb_remove_to_list_1_highlighting=suggestion resharper_vb_remove_to_list_2_highlighting=suggestion resharper_vb_replace_with_first_or_default_highlighting=suggestion resharper_vb_replace_with_last_or_default_highlighting=suggestion resharper_vb_replace_with_of_type_1_highlighting=suggestion resharper_vb_replace_with_of_type_2_highlighting=suggestion resharper_vb_replace_with_of_type_any_1_highlighting=suggestion resharper_vb_replace_with_of_type_any_2_highlighting=suggestion resharper_vb_replace_with_of_type_count_1_highlighting=suggestion resharper_vb_replace_with_of_type_count_2_highlighting=suggestion resharper_vb_replace_with_of_type_first_1_highlighting=suggestion resharper_vb_replace_with_of_type_first_2_highlighting=suggestion resharper_vb_replace_with_of_type_first_or_default_1_highlighting=suggestion resharper_vb_replace_with_of_type_first_or_default_2_highlighting=suggestion resharper_vb_replace_with_of_type_last_1_highlighting=suggestion resharper_vb_replace_with_of_type_last_2_highlighting=suggestion resharper_vb_replace_with_of_type_last_or_default_1_highlighting=suggestion resharper_vb_replace_with_of_type_last_or_default_2_highlighting=suggestion resharper_vb_replace_with_of_type_single_1_highlighting=suggestion resharper_vb_replace_with_of_type_single_2_highlighting=suggestion resharper_vb_replace_with_of_type_single_or_default_1_highlighting=suggestion resharper_vb_replace_with_of_type_single_or_default_2_highlighting=suggestion resharper_vb_replace_with_of_type_where_highlighting=suggestion resharper_vb_replace_with_single_assignment_1_highlighting=suggestion resharper_vb_replace_with_single_assignment_2_highlighting=suggestion resharper_vb_replace_with_single_call_to_any_highlighting=suggestion resharper_vb_replace_with_single_call_to_count_highlighting=suggestion resharper_vb_replace_with_single_call_to_first_highlighting=suggestion resharper_vb_replace_with_single_call_to_first_or_default_highlighting=suggestion resharper_vb_replace_with_single_call_to_last_highlighting=suggestion resharper_vb_replace_with_single_call_to_last_or_default_highlighting=suggestion resharper_vb_replace_with_single_call_to_single_highlighting=suggestion resharper_vb_replace_with_single_call_to_single_or_default_highlighting=suggestion resharper_vb_replace_with_single_or_default_highlighting=suggestion resharper_vb_simplify_linq_expression_10_highlighting=hint resharper_vb_simplify_linq_expression_1_highlighting=suggestion resharper_vb_simplify_linq_expression_2_highlighting=suggestion resharper_vb_simplify_linq_expression_3_highlighting=suggestion resharper_vb_simplify_linq_expression_4_highlighting=suggestion resharper_vb_simplify_linq_expression_5_highlighting=suggestion resharper_vb_simplify_linq_expression_6_highlighting=suggestion resharper_vb_simplify_linq_expression_7_highlighting=hint resharper_vb_simplify_linq_expression_8_highlighting=hint resharper_vb_simplify_linq_expression_9_highlighting=hint resharper_vb_string_compare_is_culture_specific_1_highlighting=warning resharper_vb_string_compare_is_culture_specific_2_highlighting=warning resharper_vb_string_compare_is_culture_specific_3_highlighting=warning resharper_vb_string_compare_is_culture_specific_4_highlighting=warning resharper_vb_string_compare_is_culture_specific_5_highlighting=warning resharper_vb_string_compare_is_culture_specific_6_highlighting=warning resharper_vb_string_compare_to_is_culture_specific_highlighting=warning resharper_vb_string_ends_with_is_culture_specific_highlighting=none resharper_vb_string_index_of_is_culture_specific_1_highlighting=warning resharper_vb_string_index_of_is_culture_specific_2_highlighting=warning resharper_vb_string_index_of_is_culture_specific_3_highlighting=warning resharper_vb_string_last_index_of_is_culture_specific_1_highlighting=warning resharper_vb_string_last_index_of_is_culture_specific_2_highlighting=warning resharper_vb_string_last_index_of_is_culture_specific_3_highlighting=warning resharper_vb_string_starts_with_is_culture_specific_highlighting=none resharper_vb_unreachable_code_highlighting=warning resharper_vb_use_array_creation_expression_1_highlighting=suggestion resharper_vb_use_array_creation_expression_2_highlighting=suggestion resharper_vb_use_first_instead_highlighting=warning resharper_vb_use_method_any_1_highlighting=suggestion resharper_vb_use_method_any_2_highlighting=suggestion resharper_vb_use_method_any_3_highlighting=suggestion resharper_vb_use_method_any_4_highlighting=suggestion resharper_vb_use_method_any_5_highlighting=suggestion resharper_vb_use_method_is_instance_of_type_highlighting=suggestion resharper_vb_use_type_of_is_operator_1_highlighting=suggestion resharper_vb_use_type_of_is_operator_2_highlighting=suggestion resharper_vb_warnings_bc400005_highlighting=warning resharper_vb_warnings_bc40000_highlighting=warning resharper_vb_warnings_bc40008_highlighting=warning resharper_vb_warnings_bc40056_highlighting=warning resharper_vb_warnings_bc42016_highlighting=warning resharper_vb_warnings_bc42025_highlighting=warning resharper_vb_warnings_bc42104_highlighting=warning resharper_vb_warnings_bc42105_bc42106_bc42107_highlighting=warning resharper_vb_warnings_bc42304_highlighting=warning resharper_vb_warnings_bc42309_highlighting=warning resharper_vb_warnings_bc42322_highlighting=warning resharper_vb_warnings_bc42349_highlighting=warning resharper_vb_warnings_bc42353_bc42354_bc42355_highlighting=warning resharper_vb_warnings_bc42356_highlighting=warning resharper_vb_warnings_bc42358_highlighting=warning resharper_vb_warnings_wme006_highlighting=warning resharper_virtual_member_call_in_constructor_highlighting=warning resharper_virtual_member_never_overridden_global_highlighting=suggestion resharper_virtual_member_never_overridden_local_highlighting=suggestion resharper_void_method_with_must_use_return_value_attribute_highlighting=warning resharper_web_config_module_not_resolved_highlighting=warning resharper_web_config_module_qualification_resolve_highlighting=warning resharper_web_config_redundant_add_namespace_tag_highlighting=warning resharper_web_config_redundant_location_tag_highlighting=warning resharper_web_config_tag_prefix_redundand_highlighting=warning resharper_web_config_type_not_resolved_highlighting=warning resharper_web_config_unused_add_tag_highlighting=warning resharper_web_config_unused_element_due_to_config_source_attribute_highlighting=warning resharper_web_config_unused_remove_or_clear_tag_highlighting=warning resharper_web_config_web_config_path_warning_highlighting=warning resharper_web_config_wrong_module_highlighting=warning resharper_web_ignored_path_highlighting=none resharper_web_mapped_path_highlighting=hint resharper_with_statement_using_error_highlighting=error resharper_wrong_expression_statement_highlighting=warning resharper_wrong_indent_size_highlighting=none resharper_wrong_metadata_use_highlighting=none resharper_wrong_public_modifier_specification_highlighting=hint resharper_wrong_require_relative_path_highlighting=hint resharper_xaml_binding_without_context_not_resolved_highlighting=hint resharper_xaml_binding_with_context_not_resolved_highlighting=warning resharper_xaml_constructor_warning_highlighting=warning resharper_xaml_dependency_property_resolve_error_highlighting=warning resharper_xaml_duplicate_style_setter_highlighting=warning resharper_xaml_dynamic_resource_error_highlighting=error resharper_xaml_element_name_reference_not_resolved_highlighting=error resharper_xaml_ignored_path_highlighting_highlighting=none resharper_xaml_index_out_of_grid_definition_highlighting=warning resharper_xaml_invalid_member_type_highlighting=error resharper_xaml_invalid_resource_target_type_highlighting=error resharper_xaml_invalid_resource_type_highlighting=error resharper_xaml_invalid_type_highlighting=error resharper_xaml_language_level_highlighting=error resharper_xaml_mapped_path_highlighting_highlighting=hint resharper_xaml_missing_grid_index_highlighting=warning resharper_xaml_path_error_highlighting=warning resharper_xaml_redundant_attached_property_highlighting=warning resharper_xaml_redundant_collection_property_highlighting=warning resharper_xaml_redundant_freeze_attribute_highlighting=warning resharper_xaml_redundant_grid_definitions_highlighting=warning resharper_xaml_redundant_grid_span_highlighting=warning resharper_xaml_redundant_modifiers_attribute_highlighting=warning resharper_xaml_redundant_namespace_alias_highlighting=warning resharper_xaml_redundant_name_attribute_highlighting=warning resharper_xaml_redundant_property_type_qualifier_highlighting=warning resharper_xaml_redundant_resource_highlighting=warning resharper_xaml_redundant_styled_value_highlighting=warning resharper_xaml_redundant_xamarin_forms_class_declaration_highlighting=warning resharper_xaml_routed_event_resolve_error_highlighting=warning resharper_xaml_static_resource_not_resolved_highlighting=warning resharper_xaml_style_invalid_target_type_highlighting=error resharper_xaml_unexpected_text_token_highlighting=error resharper_xaml_xaml_duplicate_device_family_type_view_highlighting_highlighting=error resharper_xaml_xaml_mismatched_device_family_view_clr_name_highlighting_highlighting=warning resharper_xaml_xaml_relative_source_default_mode_warning_highlighting_highlighting=warning resharper_xaml_xaml_unknown_device_family_type_highlighting_highlighting=warning resharper_xaml_xaml_xamarin_forms_data_type_and_binding_context_type_mismatched_highlighting_highlighting=warning resharper_xaml_x_key_attribute_disallowed_highlighting=error resharper_xml_doc_comment_syntax_problem_highlighting=warning resharper_xunit_xunit_test_with_console_output_highlighting=warning resharper_x_unit1000_highlighting=error resharper_x_unit1001_highlighting=error resharper_x_unit1002_highlighting=error resharper_x_unit1003_highlighting=error resharper_x_unit1004_highlighting=hint resharper_x_unit1005_highlighting=warning resharper_x_unit1006_highlighting=warning resharper_x_unit1007_highlighting=error resharper_x_unit1008_highlighting=warning resharper_x_unit1009_highlighting=error resharper_x_unit1010_highlighting=error resharper_x_unit1011_highlighting=error resharper_x_unit1012_highlighting=warning resharper_x_unit1013_highlighting=warning resharper_x_unit1014_highlighting=warning resharper_x_unit1015_highlighting=error resharper_x_unit1016_highlighting=error resharper_x_unit1017_highlighting=error resharper_x_unit1018_highlighting=error resharper_x_unit1019_highlighting=error resharper_x_unit1020_highlighting=error resharper_x_unit1021_highlighting=warning resharper_x_unit1022_highlighting=error resharper_x_unit1023_highlighting=error resharper_x_unit1024_highlighting=error resharper_x_unit1025_highlighting=warning resharper_x_unit1026_highlighting=warning resharper_x_unit2000_highlighting=warning resharper_x_unit2001_highlighting=none resharper_x_unit2002_highlighting=warning resharper_x_unit2003_highlighting=warning resharper_x_unit2004_highlighting=warning resharper_x_unit2005_highlighting=warning resharper_x_unit2006_highlighting=warning resharper_x_unit2007_highlighting=warning resharper_x_unit2008_highlighting=warning resharper_x_unit2009_highlighting=warning resharper_x_unit2010_highlighting=warning resharper_x_unit2011_highlighting=warning resharper_x_unit2012_highlighting=warning resharper_x_unit2013_highlighting=warning resharper_x_unit2014_highlighting=error resharper_x_unit2015_highlighting=warning resharper_x_unit2016_highlighting=error resharper_x_unit2017_highlighting=warning resharper_x_unit2018_highlighting=warning resharper_x_unit2019_highlighting=none resharper_x_unit3000_highlighting=error resharper_x_unit3001_highlighting=error [{*.har,*.jsb2,*.jsb3,*.json,.babelrc,.eslintrc,.stylelintrc,bowerrc,jest.config}] indent_style=space indent_size=2 [*.scss] indent_style=space indent_size=2 [*.js.map] indent_style=space indent_size=2 [*.{appxmanifest,asax,ascx,aspx,build,cs,cshtml,dtd,master,nuspec,razor,resw,resx,skin,vb,xaml,xamlx,xoml,xsd}] indent_style=space indent_size=4 tab_width=4 ================================================ FILE: .gitattributes ================================================ ############################################################################### # Set default behavior to automatically normalize line endings. ############################################################################### * text=auto ############################################################################### # Set default behavior for command prompt diff. # # This is need for earlier builds of msysgit that does not have it on by # default for csharp files. # Note: This is only used by command line ############################################################################### #*.cs diff=csharp ############################################################################### # Set the merge driver for project and solution files # # Merging from the command prompt will add diff markers to the files if there # are conflicts (Merging from VS is not affected by the settings below, in VS # the diff markers are never inserted). Diff markers may cause the following # file extensions to fail to load in VS. An alternative would be to treat # these files as binary and thus will always conflict and require user # intervention with every merge. To do so, just uncomment the entries below ############################################################################### #*.sln merge=binary #*.csproj merge=binary #*.vbproj merge=binary #*.vcxproj merge=binary #*.vcproj merge=binary #*.dbproj merge=binary #*.fsproj merge=binary #*.lsproj merge=binary #*.wixproj merge=binary #*.modelproj merge=binary #*.sqlproj merge=binary #*.wwaproj merge=binary ############################################################################### # behavior for image files # # image files are treated as binary by default. ############################################################################### #*.jpg binary #*.png binary #*.gif binary ############################################################################### # diff behavior for common document formats # # Convert binary document formats to text before diffing them. This feature # is only available from the command line. Turn it on by uncommenting the # entries below. ############################################################################### #*.doc diff=astextplain #*.DOC diff=astextplain #*.docx diff=astextplain #*.DOCX diff=astextplain #*.dot diff=astextplain #*.DOT diff=astextplain #*.pdf diff=astextplain #*.PDF diff=astextplain #*.rtf diff=astextplain #*.RTF diff=astextplain ================================================ FILE: .github/CONTRIBUTING.md ================================================ # How to contribute The easiest way to contribute is to open an issue and start a discussion. Then we can decide if and how a feature or a change could be implemented and if you should submit a pull requests with code changes. Also read this first: [Being a good open source citizen](https://hackernoon.com/being-a-good-open-source-citizen-9060d0ab9732#.x3hocgw85) ## Found an issue or a bug? Please start a discussion on the [core repo issue tracker](https://github.com/alexhiggins732/IdentityServer4/issues). ## Filing issues The best way to get your bug fixed is to be as detailed as you can be about the problem. Providing a minimal project with steps to reproduce the problem is ideal. Here are questions you can answer before you file a bug to make sure you're not missing any important information. 1. Did you read the [documentation](https://identityserver8.readthedocs.io/en/latest/)? 2. Did you include the snippet of broken code in the issue? 3. What are the *EXACT* steps to reproduce this problem? 4. Did you enable [logging](https://identityserver8.readthedocs.io/en/latest/topics/logging.html)? GitHub supports [markdown](http://github.github.com/github-flavored-markdown/), so when filing bugs make sure you check the formatting before clicking submit. ## Contributing code and content You will need to sign a contributor license agreement (CLA) before submitting your pull request. The first time you submit a PR, a bot will take you through that process. Please make sure to include tests, that cover the changes/additions you made to the code base. Make sure you can build the code. Familiarize yourself with the project workflow and our coding conventions. If you don't know what a pull request is read this article: https://help.github.com/articles/using-pull-requests. Before submitting a feature or substantial code contribution please discuss it with the team and ensure it follows the product roadmap. Here's a list of blog posts that are worth reading before doing a pull request: * [Open Source Contribution Etiquette](http://tirania.org/blog/archive/2010/Dec-31.html) by Miguel de Icaza * [Don't "Push" Your Pull Requests](http://www.igvita.com/2011/12/19/dont-push-your-pull-requests/) by Ilya Grigorik. * [10 tips for better Pull Requests](http://blog.ploeh.dk/2015/01/15/10-tips-for-better-pull-requests/) by Mark Seemann * [How to write the perfect pull request](https://github.com/blog/1943-how-to-write-the-perfect-pull-request) by GitHub ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: alexhiggins732 patreon: alexhiggins732 open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: https://www.paypal.me/alexhiggins732 ================================================ FILE: .github/ISSUE_TEMPLATE/Question.md ================================================ --- name: Question about: Ask a question. title: '' labels: question assignees: skoruba --- ### Question ### Minimal working example ```csharp // ``` ### Relevant parts of the log file ``` ``` ================================================ FILE: .github/ISSUE_TEMPLATE/bug_report.md ================================================ --- name: Bug report about: Create a report to help us improve title: '' labels: bug report --- ### Describe the bug A clear and concise description of what the bug is. ### To Reproduce Steps to reproduce the behavior: ### Relevant parts of the log file ``` ``` ================================================ FILE: .github/ISSUE_TEMPLATE/feature_request.md ================================================ --- name: Feature request about: Suggest an idea for this project title: '' labels: enhancement --- **Is your feature request related to a problem? Please describe.** A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] **Describe the solution you'd like** A clear and concise description of what you want to happen. **Describe alternatives you've considered** A clear and concise description of any alternative solutions or features you've considered. **Additional context** Add any other context or screenshots about the feature request here. ================================================ FILE: .github/PULL_REQUEST_TEMPLATE.md ================================================ --- name: Bug report about: Create a report to help us improve labels: bug report --- **We can only help you if you are on the latest version.** Please only use the issue tracker for bug reports and/or feature requests. For general security questions, or free or commercial support options do __not__ use the issue tracker and instead see [here](http://identityserver8.readthedocs.io/en/latest/intro/support.html) for more details. For bug reports, include the relevant log files related to your issue. See here how to enable [logging](https://identityserver8.readthedocs.io/en/latest/topics/logging.html). Delete this line once you have. Finally, please keep the issue concise and to the point. If you paste in more code than the text for the issue you are reporting then we will most likely not read it. ### Issue / Steps to reproduce the problem ### Relevant parts of the log file ``` ``` ================================================ FILE: .github/dependabot.yml ================================================ # To get started with Dependabot version updates, you'll need to specify which # package ecosystems to update and where the package manifests are located. # Please see the documentation for all configuration options: # https://docs.github.com/code-security/dependabot/dependabot-version-updates/configuration-options-for-the-dependabot.yml-file version: 2 updates: - package-ecosystem: "nuget" # See documentation for possible values directory: "/" # Location of package manifests schedule: interval: "daily" open-pull-requests-limit: 25 ================================================ FILE: .github/workflows/codeql.yml ================================================ # For most projects, this workflow file will not need changing; you simply need # to commit it to your repository. # # You may wish to alter this file to override the set of languages analyzed, # or to provide custom queries or build logic. # # ******** NOTE ******** # We have attempted to detect the languages in your repository. Please check # the `language` matrix defined below to confirm you have the correct set of # supported CodeQL languages. # name: "CodeQL" on: push: branches: [ "master", "develop", "release/*" ] paths-ignore: - '**/README.md' - '**/docs' - '.github/**' - "docs/**" - ".git/*" - ".vs/*" - ".config/*" - ".github/*" - "Directory.Build.props" - "Directory.Build.targets" - "Directory.Build.props" - "docker-compose.yml" - "docker-compose.override.yml" - "docker-compose.vs.debug.yml" - "docker-compose.vs.release.yml" - "docker-compose.dcrpoj" - "**/*.sln" - "global.json" - "IdentityServer8.DotNet.ruleset" - "LICENSCE" - "version.json" - "Nuget.config" - "SECURITY.md" - "SPONSORS.md" - "README.md" - "samples" - "nuget" pull_request: branches: [ "master", "develop", "release/*" ] schedule: - cron: '23 4 * * 1' jobs: analyze: name: Analyze # Runner size impacts CodeQL analysis time. To learn more, please see: # - https://gh.io/recommended-hardware-resources-for-running-codeql # - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/using-larger-runners # Consider using larger runners for possible analysis time improvements. runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} permissions: # required for all workflows security-events: write # only required for workflows in private repositories actions: read contents: read strategy: fail-fast: false matrix: language: [ 'csharp', 'javascript-typescript' ] # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@v4 # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@v3 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) #- name: Autobuild # uses: github/codeql-action/autobuild@v3 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. - name: Restore dependencies if: ${{ (matrix.language == 'csharp') }} run: dotnet restore src/IdentityServer8.sln - name: Build if: ${{ (matrix.language == 'csharp') }} run: dotnet build src/IdentityServer8.sln --configuration Release --no-restore - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@v3 with: category: "/language:${{matrix.language}}" ================================================ FILE: .github/workflows/develop.yml ================================================ # This workflow will build a .NET project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net name: Develop on: push: branches: - develop paths-ignore: - '**/README.md' - '**/docs' - '.github/**' - "docs/**" - ".git/*" - ".vs/*" - ".config/*" - ".github/*" - "Directory.Build.props" - "Directory.Build.targets" - "Directory.Build.props" - "docker-compose.yml" - "docker-compose.override.yml" - "docker-compose.vs.debug.yml" - "docker-compose.vs.release.yml" - "docker-compose.dcrpoj" - "**/*.sln" - "global.json" - "IdentityServer8.DotNet.ruleset" - "LICENSCE" - "Nuget.config" - "SECURITY.md" - "SPONSORS.md" - "README.md" - "samples" - "nuget" jobs: build: strategy: fail-fast: false matrix: runs-on: [macOS-latest, ubuntu-latest, windows-latest] name: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - uses: dotnet/nbgv@master id: nbgv - name: Display Package Version run: echo "PackageVersion=${{ steps.nbgv.outputs.SemVer2 }}" - name: Restore dependencies run: dotnet restore src/IdentityServer8.sln - name: Build run: dotnet build src/IdentityServer8.sln --configuration Release --no-restore -p:Version=${{ steps.nbgv.outputs.SemVer2 }} - name: Test run: dotnet test src/IdentityServer8.sln --configuration Release --no-build --verbosity normal /p:CollectCoverage=true --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 env: token: ${{ secrets.CODECOV_TOKEN }} slug: alexhiggins732/IdentityServer8 CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Push if: ${{ (github.event_name == 'push') && (runner.os == 'Windows') }} run: dotnet nuget push .\nuget\*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} ================================================ FILE: .github/workflows/master.yml ================================================ # This workflow will build a .NET project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net name: Master on: push: branches: - master paths-ignore: - '**/README.md' - '**/docs' - '.github/**' - "docs/**" - ".git/*" - ".vs/*" - ".config/*" - ".github/*" - "Directory.Build.props" - "Directory.Build.targets" - "Directory.Build.props" - "docker-compose.yml" - "docker-compose.override.yml" - "docker-compose.vs.debug.yml" - "docker-compose.vs.release.yml" - "docker-compose.dcrpoj" - "**/*.sln" - "global.json" - "IdentityServer8.DotNet.ruleset" - "LICENSCE" - "Nuget.config" - "SECURITY.md" - "SPONSORS.md" - "README.md" - "samples" - "nuget" jobs: build: strategy: fail-fast: false matrix: runs-on: [macOS-latest, ubuntu-latest, windows-latest] name: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - uses: dotnet/nbgv@master id: nbgv - name: Display Package Version run: echo "PackageVersion=${{ steps.nbgv.outputs.SimpleVersion }}" - name: Restore dependencies run: dotnet restore src/IdentityServer8.sln - name: Build run: dotnet build src/IdentityServer8.sln --configuration Release --no-restore -p:Version=${{ steps.nbgv.outputs.SimpleVersion }} - name: Test run: dotnet test src/IdentityServer8.sln --configuration Release --no-build --verbosity normal /p:CollectCoverage=true --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 env: token: ${{ secrets.CODECOV_TOKEN }} slug: alexhiggins732/IdentityServer8 CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Push if: ${{ (github.event_name == 'push') && (runner.os == 'Windows') }} run: dotnet nuget push .\nuget\*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} ================================================ FILE: .github/workflows/pre-release.yml ================================================ # This workflow will build a .NET project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net name: CI on: push: branches-ignore: - develop - release/* - master paths-ignore: - '**/README.md' - '**/docs' - '.github/**' - "docs/**" - ".git/*" - ".vs/*" - ".config/*" - ".github/*" - "Directory.Build.props" - "Directory.Build.targets" - "Directory.Build.props" - "docker-compose.yml" - "docker-compose.override.yml" - "docker-compose.vs.debug.yml" - "docker-compose.vs.release.yml" - "docker-compose.dcrpoj" - "**/*.sln" - "global.json" - "IdentityServer8.DotNet.ruleset" - "LICENSCE" - "Nuget.config" - "SECURITY.md" - "SPONSORS.md" - "README.md" - "samples" - "nuget" jobs: build: strategy: fail-fast: false matrix: runs-on: [macOS-latest, ubuntu-latest, windows-latest] name: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - uses: dotnet/nbgv@master id: nbgv - name: Display Package Version run: echo "PackageVersion=${{ steps.nbgv.outputs.SimpleVersion }}-ci.${{ steps.nbgv.outputs.VersionRevision }}" - name: Restore dependencies run: dotnet restore src/IdentityServer8.sln - name: Build run: dotnet build src/IdentityServer8.sln --configuration Release --no-restore -p:Version=${{ steps.nbgv.outputs.SimpleVersion }}-ci.${{ steps.nbgv.outputs.VersionRevision }} - name: Test run: dotnet test src/IdentityServer8.sln --configuration Release --no-build --verbosity normal /p:CollectCoverage=true --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 env: token: ${{ secrets.CODECOV_TOKEN }} slug: alexhiggins732/IdentityServer8 CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} ================================================ FILE: .github/workflows/release.yml ================================================ # This workflow will build a .NET project # For more information see: https://docs.github.com/en/actions/automating-builds-and-tests/building-and-testing-net name: Release on: push: branches: - release/* paths-ignore: - '**/README.md' - '**/docs' - '.github/**' - "docs/**" - ".git/*" - ".vs/*" - ".config/*" - ".github/*" - "Directory.Build.props" - "Directory.Build.targets" - "Directory.Build.props" - "docker-compose.yml" - "docker-compose.override.yml" - "docker-compose.vs.debug.yml" - "docker-compose.vs.release.yml" - "docker-compose.dcrpoj" - "**/*.sln" - "global.json" - "IdentityServer8.DotNet.ruleset" - "LICENSCE" - "Nuget.config" - "SECURITY.md" - "SPONSORS.md" - "README.md" - "samples" - "nuget" jobs: build: strategy: fail-fast: false matrix: runs-on: [macOS-latest, ubuntu-latest, windows-latest] name: ${{ matrix.runs-on }} runs-on: ${{ matrix.runs-on }} steps: - uses: actions/checkout@v3 with: fetch-depth: 0 - name: Setup .NET uses: actions/setup-dotnet@v3 with: dotnet-version: 8.0.x - uses: dotnet/nbgv@master id: nbgv - name: Display Package Version run: echo "PackageVersion=${{ steps.nbgv.outputs.SemVer2 }}" - name: Restore dependencies run: dotnet restore src/IdentityServer8.sln - name: Build run: dotnet build src/IdentityServer8.sln --configuration Release --no-restore -p:Version=${{ steps.nbgv.outputs.SemVer2 }} - name: Test run: dotnet test src/IdentityServer8.sln --configuration Release --no-build --verbosity normal /p:CollectCoverage=true --collect:"XPlat Code Coverage" -- DataCollectionRunSettings.DataCollectors.DataCollector.Configuration.Format=cobertura - name: Upload coverage reports to Codecov uses: codecov/codecov-action@v4.0.1 env: token: ${{ secrets.CODECOV_TOKEN }} slug: alexhiggins732/IdentityServer8 CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }} - name: Push if: ${{ (github.event_name == 'push') && (runner.os == 'Windows') }} run: dotnet nuget push .\nuget\*.nupkg --skip-duplicate --source https://api.nuget.org/v3/index.json --api-key ${{ secrets.NUGET_API_KEY }} ================================================ FILE: .gitignore ================================================ ## Ignore Visual Studio temporary files, build results, and ## files generated by popular Visual Studio add-ons. # Rider .idea # User-specific files *.suo *.user *.userosscache *.sln.docstates # User-specific files (MonoDevelop/Xamarin Studio) *.userprefs # Build results [Dd]ebug/ [Dd]ebugPublic/ [Rr]elease/ [Rr]eleases/ x64/ x86/ bld/ [Bb]in/ [Oo]bj/ [Ll]og/ # Visual Studio cache/options directory .vs/ project.lock.json # Uncomment if you have tasks that create the project's static files in wwwroot #wwwroot/ # MSTest test Results [Tt]est[Rr]esult*/ [Bb]uild[Ll]og.* # NUNIT *.VisualState.xml TestResult.xml # Build Results of an ATL Project [Dd]ebugPS/ [Rr]eleasePS/ dlldata.c # DNX project.lock.json project.fragment.lock.json artifacts/ Properties/launchSettings.json *_i.c *_p.c *_i.h *.ilk *.meta *.obj *.pch *.pdb *.pgc *.pgd *.rsp *.sbr *.tlb *.tli *.tlh *.tmp *.tmp_proj *.log *.vspscc *.vssscc .builds *.pidb *.svclog *.scc # Chutzpah Test files _Chutzpah* # Visual C++ cache files ipch/ *.aps *.ncb *.opendb *.opensdf *.sdf *.cachefile *.VC.db *.VC.VC.opendb # Visual Studio profiler *.psess *.vsp *.vspx *.sap # TFS 2012 Local Workspace $tf/ # Guidance Automation Toolkit *.gpState # ReSharper is a .NET coding add-in _ReSharper*/ *.[Rr]e[Ss]harper *.DotSettings.user # JustCode is a .NET coding add-in .JustCode # TeamCity is a build add-in _TeamCity* # DotCover is a Code Coverage Tool *.dotCover # Visual Studio code coverage results *.coverage *.coveragexml # NCrunch _NCrunch_* .*crunch*.local.xml nCrunchTemp_* # MightyMoose *.mm.* AutoTest.Net/ # Web workbench (sass) .sass-cache/ # Installshield output folder [Ee]xpress/ # DocProject is a documentation generator add-in DocProject/buildhelp/ DocProject/Help/*.HxT DocProject/Help/*.HxC DocProject/Help/*.hhc DocProject/Help/*.hhk DocProject/Help/*.hhp DocProject/Help/Html2 DocProject/Help/html # Click-Once directory publish/ # Publish Web Output *.[Pp]ublish.xml *.azurePubxml # TODO: Comment the next line if you want to checkin your web deploy settings # but database connection strings (with potential passwords) will be unencrypted *.pubxml *.publishproj # Microsoft Azure Web App publish settings. Comment the next line if you want to # checkin your Azure Web App publish settings, but sensitive information contained # in these scripts will be unencrypted PublishScripts/ # NuGet Packages *.nupkg # The packages folder can be ignored because of Package Restore **/packages/* # except build/, which is used as an MSBuild target. !**/packages/build/ # Uncomment if necessary however generally it will be regenerated when needed #!**/packages/repositories.config # NuGet v3's project.json files produces more ignoreable files *.nuget.props *.nuget.targets # Microsoft Azure Build Output csx/ *.build.csdef # Microsoft Azure Emulator ecf/ rcf/ # Windows Store app package directories and files AppPackages/ BundleArtifacts/ Package.StoreAssociation.xml _pkginfo.txt # Visual Studio cache files # files ending in .cache can be ignored *.[Cc]ache # but keep track of directories ending in .cache !*.[Cc]ache/ # Others ClientBin/ ~$* *~ *.dbmdl *.dbproj.schemaview *.jfm *.pfx *.publishsettings node_modules/ bower_components/ orleans.codegen.cs # RIA/Silverlight projects Generated_Code/ # Backup & report files from converting an old project file # to a newer Visual Studio version. Backup files are not needed, # because we have git ;-) _UpgradeReport_Files/ Backup*/ UpgradeLog*.XML UpgradeLog*.htm # SQL Server files *.mdf *.ldf # Business Intelligence projects *.rdl.data *.bim.layout *.bim_*.settings # Microsoft Fakes FakesAssemblies/ # GhostDoc plugin setting file *.GhostDoc.xml # Node.js Tools for Visual Studio .ntvs_analysis.dat # Visual Studio 6 build log *.plg # Visual Studio 6 workspace options file *.opt docs/_build/ # Local .NET CLI tools tools/ # Visual Studio Code workspace options .vscode # IdentityServer temp files IdentityServer8_log.txt tempkey.rsa samples/KeyManagement/FileSystem/dataprotectionkeys/ samples/KeyManagement/FileSystem/signingkeys/ workspace.xml src/IdentityServer8/host/identityserver.db tempkey.jwk /nuget # Paket dependency manager .paket/paket.exe paket-files/ # FAKE - F# Make .fake/ # JetBrains Rider .idea/ *.sln.iml # CodeRush .cr/ # Python Tools for Visual Studio (PTVS) __pycache__/ *.pyc # Cake - Uncomment if you are using it # tools/ /src/IdentityServer8.Admin/appsettings.production.json /src/IdentityServer8.Admin/Scripts/Libs/ /src/IdentityServer8.Admin/Data/Migrations/ # Don't ignore these log folders !/src/IdentityServer8.Admin.UI/Resources/Views/Log/ !/src/IdentityServer8.Admin.BusinessLogic/Dtos/Log/ !**/Views/Log/ !/src/IdentityServer8.Admin.BusinessLogic/Events/Log/ /src/IdentityServer8.Admin.Api/appsettings.Production.json /shared/nginx/certs/ lib/ ================================================ FILE: .readthedocs.yaml ================================================ # .readthedocs.yaml # Read the Docs configuration file # See https://docs.readthedocs.io/en/stable/config-file/v2.html for details # Required version: 2 # Set the OS, Python version and other tools you might need build: os: ubuntu-22.04 tools: python: "3.12" # You can also specify other tool versions: # nodejs: "19" # rust: "1.64" # golang: "1.19" # Build documentation in the "docs/" directory with Sphinx sphinx: configuration: docs/conf.py # Optionally build your docs in additional formats such as PDF and ePub # formats: # - pdf # - epub # Optional but recommended, declare the Python requirements required # to build your documentation # See https://docs.readthedocs.io/en/stable/guides/reproducible-builds.html python: install: - requirements: docs/requirements.txt ================================================ FILE: Directory.Build.props ================================================ net8.0 8.0.4 8.0.4 HigginsSoft.$(MSBuildProjectName) $(MSBuildProjectName) enable LICENSE false $(NoWarn);CS0618;SYSLIB0023;SYSLIB0020;EF1001 true $(SolutionDir)../key.snk OpenID Connect and OAuth 2.0 Framework for ASP.NET Core Copyright 2024 HigginsSoft. Alexander Higgins HigginsSoft, Alexander Higgins, Brock Allen, Dominick Baier OAuth2 OAuth 2.0 OpenID Connect Security Identity IdentityServer Admin IdentityServer8 OpenIDConnect https://github.com/alexhiggins732/IdentityServer8 git https://github.com/alexhiggins732/IdentityServer8/releases true $(AllowedOutputExtensionsInPackageBuildOutputFolder);.pdb icon.jpg true false false true true Enable latest false false $(SolutionDir)../nuget false false README.md true portable true snupkg true True \ True \ True \ ================================================ FILE: Directory.Build.targets ================================================ ================================================ FILE: Directory.Packages.props ================================================ true true 8.0.0 8.0.1 8.0.0 8.0.0 8.0.0 8.0.0-preview.2.23619.3 2.59.0 8.0.0 $(NoWarn);AD0001;ASP0003;ASP0004;ASP0005;ASP0007;ASP0020;ASP0021;ASP0022;ASP0024 true runtime; build; native; contentfiles; analyzers; buildtransitive all all runtime; build; native; contentfiles; analyzers; buildtransitive all runtime; build; native; contentfiles; analyzers; buildtransitive runtime; build; native; contentfiles; analyzers; buildtransitive all ================================================ FILE: GitReleaseManager.yaml ================================================ create: include-footer: false footer-heading: footer-content: footer-includes-milestone: false milestone-replace-text: export: include-created-date-in-title: false created-date-string-format: perform-regex-removal: false regex-text: multiline-regex: false issue-labels-include: - bug - new feature - enhancement - breaking change issue-labels-exclude: - Internal Refactoring ================================================ FILE: IdentityServer8.DotNet.ruleset ================================================  ================================================ FILE: LICENSE ================================================ Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS APPENDIX: How to apply the Apache License to your work. To apply the Apache License to your work, attach the following boilerplate notice, with the fields enclosed by brackets "{}" replaced with your own identifying information. (Don't include the brackets!) The text should be enclosed in the appropriate comment syntax for the file format. We also recommend that a file or class name and description of purpose be included on the same "printed page" as the copyright notice for easier identification within third-party archives. Copyright 2018, Brock Allen, Dominick Baier Copyright 2024, HigginsSoft, Alexander Higgins Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. ================================================ FILE: LicenseHeader.txt ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ================================================ FILE: NuGet.config ================================================ ================================================ FILE: README.md ================================================ # Identity Server 8 update This project is a DotNet 8 revival of the Identity Server 4 and Identity Server 4 Admin UI, for Open ID Connect (OIDC) and OAuth, which was archived when .NET Core 3.1 reached end of support. The latest verion, 8.0.4, is now available on NuGet. It contains [hundreds of security and bug fixes](https://github.com/alexhiggins732/IdentityServer8/blob/master/docs/CHANGELOG.md) from the original Identity Server 4 project. It is recommend you update all previous version, 4 or 8, to the latest version to ensure you have the latest security updates. - [Documentation](http://identityserver8.readthedocs.io/) - [Support](https://identityserver8.readthedocs.io/en/latest/into/support.html) - [Gitter Chat](https://app.gitter.im/#/room/#identityserver8:gitter.im) [HigginsSoft.IdentityServer8 and Admin UI Nuget Packages](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8) are available here for use in DotNet 8. All new development in the archived repository has moved to a paid commercial version in the [Duende Software](https://github.com/duendesoftware) organization. See [here](https://duendesoftware.com/products/identityserver) for more details on the commerica version. This repository will be maintained for bug fixes and security updates for the .NET 8 version of Identity Server 4. The source code and unit tests will be updated to use the latest .NET 8 features and best practices. Once the source code and unit tests are stabilized, the documentation will be updated to reflect the changes. General speaking, the existing documentation for Identity Server 4 will be valid for this repository, but the new features and changes will be documented in the new documentation. In the meantime, NuGet packages will be published to the [IdentityServer8 NuGet feed](https://www.nuget.org/profiles/IdentityServer8) and the [IdentityServer8 MyGet feed](https://www.myget.org/F/identityserver8/api/v3/index.json). ## Build Status And Stats [![Master | Build](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/master.yml/badge.svg)](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/master.yml) [![Release|Build](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/release.yml/badge.svg)](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/release.yml) [![Develop|Build](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/develop.yml/badge.svg)](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/develop.yml) [![CI/CD|Build](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/pre-release.yml/badge.svg)](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/pre-release.yml) ## Code Coverage [![Master | Build](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/master.yml/badge.svg)](https://img.shields.io/codecov/c/github/alexhiggins732/identityserver8) [![Master|CodeQL](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/codeql.yml/badge.svg)](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/codeql.yml) [![Develop|Build](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/develop.yml/badge.svg)](https://img.shields.io/codecov/c/github/alexhiggins732/identityserver8/tree/develop) [![Master|CodeQL](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/codeql.yml/badge.svg?branch=develop)](https://github.com/alexhiggins732/IdentityServer8/actions/workflows/codeql.yml?branch=develop) ## Documentation [![Documentation Status](https://readthedocs.org/projects/identityserver8/badge/?version=latest)](https://identityserver8.readthedocs.io/en/latest/?badge=latest) [Read the docs - identityserver8.readthedocs.io ](http://identityserver8.readthedocs.io/) ## Nuget Packages ### Identity Server 8 |Package|| | ------------- | ------------- | |[HigginsSoft.IdentityServer8](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8)| |[HigginsSoft.IdentityServer8.Storage](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Storage)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Storage)| |[HigginsSoft.IdentityServer8.EntityFramework](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.EntityFramework)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.EntityFramework)| |[HigginsSoft.IdentityServer8.EntityFramework.Storage](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.EntityFramework.Storage)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.EntityFramework.Storage)| |[HigginsSoft.IdentityServer8.AspNetIdentity](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.AspNetIdentity)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.AspNetIdentity)| | | | ### Identity Server 8 Administration UI |Package|| | ------------- | ------------- | |[HigginsSoft.IdentityServer8.Shared](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Shared)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Shared)| |[HigginsSoft.IdentityServer8.Admin.UI](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.UI)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.UI)| |[HigginsSoft.IdentityServer8.Shared.Configuration](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Shared.Configuration)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Shared.Configuration)| |[HigginsSoft.IdentityServer8.Admin.Api](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.Api)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.Api)| |[HigginsSoft.IdentityServer8.Admin](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin)| |[HigginsSoft.IdentityServer8.Admin.BusinessLogic.Identity](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.BusinessLogic.Identity)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.BusinessLogic.Identity)| |[HigginsSoft.IdentityServer8.Admin.EntityFramework](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.EntityFramework)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.EntityFramework)| |[HigginsSoft.IdentityServer8.Admin.EntityFramework.Identity](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.EntityFramework.Identity)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.EntityFramework.Identity)| |[HigginsSoft.IdentityServer8.Admin.BusinessLogic.Shared](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.BusinessLogic.Shared)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.BusinessLogic.Shared)| |[HigginsSoft.IdentityServer8.Admin.EntityFramework.Extensions](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.EntityFramework.Extensions)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.EntityFramework.Extensions)| |[HigginsSoft.IdentityServer8.Admin.EntityFramework.PostgreSQL](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.EntityFramework.PostgreSQL)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.EntityFramework.PostgreSQL)| |[HigginsSoft.IdentityServer8.Admin.EntityFramework.MySql](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.EntityFramework.MySql)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.EntityFramework.MySql)| |[HigginsSoft.IdentityServer8.Admin.EntityFramework.SqlServer](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.EntityFramework.SqlServer)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.EntityFramework.SqlServer)| |[HigginsSoft.IdentityServer8.Admin.EntityFramework.Shared](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.EntityFramework.Shared)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.EntityFramework.Shared)| |[HigginsSoft.IdentityServer8.Admin.BusinessLogic](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.BusinessLogic)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.BusinessLogic)| |[HigginsSoft.IdentityServer8.Admin.EntityFramework.Configuration](https://www.nuget.org/packages?q=HigginsSoft.IdentityServer8.Admin.EntityFramework.Configuration)|![NuGet Downloads](https://img.shields.io/nuget/dt/HigginsSoft.IdentityServer8.Admin.EntityFramework.Configuration)| | | | ## What's New View the [CHANGELOG](docs/CHANGELOG.md) for the latest changes. ## About IdentityServer8 [](https://dotnetfoundation.org/projects?searchquery=IdentityServer&type=project) IdentityServer is a free, open source [OpenID Connect](http://openid.net/connect/) and [OAuth 2.0](https://tools.ietf.org/html/rfc6749) framework for ASP.NET Core. Founded and maintained by [Dominick Baier](https://twitter.com/leastprivilege) and [Brock Allen](https://twitter.com/brocklallen), IdentityServer8 incorporates all the protocol implementations and extensibility points needed to integrate token-based authentication, single-sign-on and API access control in your applications. IdentityServer8 is officially [certified](https://openid.net/certification/) by the [OpenID Foundation](https://openid.net) and thus spec-compliant and interoperable. It is part of the [.NET Foundation](https://www.dotnetfoundation.org/), and operates under their [code of conduct](https://www.dotnetfoundation.org/code-of-conduct). It is licensed under [Apache 2](https://opensource.org/licenses/Apache-2.0) (an OSI approved license). For project documentation, please visit [readthedocs](https://IdentityServer8.readthedocs.io). ## Branch structure Active development happens on the main branch. This always contains the latest version. Each (pre-) release is tagged with the corresponding version. The [aspnetcore1](https://github.com/alexhiggins732/IdentityServer8/tree/aspnetcore1) and [aspnetcore2](https://github.com/alexhiggins732/IdentityServer8/tree/aspnetcore2) branches contain the latest versions of the older ASP.NET Core based versions. ## How to build * [Install]([https://www.microsoft.com/net/download/core#/current](https://dotnet.microsoft.com/en-us/download#/current) the latest .NET 8 SDK * Install Git * Clone this repo * Run `dotnet build src/identityserver8.slm` or `build.sh` in the root of the cloned repo. ## Documentation For project documentation, please visit [readthedocs](https://IdentityServer8.readthedocs.io). See [here](http://docs.identityserver8.io/en/aspnetcore1/) for the 1.x docs, and [here](http://docs.identityserver8.io/en/aspnetcore2/) for the 2.x docs. ## Bug reports and feature requests Please use the [issue tracker](https://github.com/alexhiggins732/IdentityServer8/issues) for that. We only support the latest version for free. For older versions, you can get a commercial support agreement with us. ## Commercial and Community Support If you need help with implementing IdentityServer8 or your security architecture in general, there are both free and commercial support options. See [here](https://IdentityServer8.readthedocs.io/en/latest/intro/support.html) for more details. ## Sponsorship If you are a fan of the project or a company that relies on IdentityServer, you might want to consider sponsoring. This will help us devote more time to answering questions and doing feature development. If you are interested please head to our [Patreon](https://www.patreon.com/identityserver) page which has further details. ### Platinum Sponsors [](https://udelt.no) [](https://github.com/dotnet-at-microsoft) ### Corporate Sponsors [Ritter Insurance Marketing](https://www.ritterim.com) [ExtraNetUserManager](https://www.extranetusermanager.com/) [Knab](https://www.knab.nl/) You can see a list of our current sponsors [here](https://github.com/alexhiggins732/IdentityServer8/blob/main/SPONSORS.md) - and for companies we have some nice advertisement options as well. ## Acknowledgements IdentityServer8 is built using the following great open source projects and free services: * [ASP.NET Core](https://github.com/dotnet/aspnetcore) * [Bullseye](https://github.com/adamralph/bullseye) * [SimpleExec](https://github.com/adamralph/simple-exec) * [MinVer](https://github.com/adamralph/minver) * [Json.Net](http://www.newtonsoft.com/json) * [XUnit](https://xunit.github.io/) * [Fluent Assertions](http://www.fluentassertions.com/) * [GitReleaseManager](https://github.com/GitTools/GitReleaseManager) ..and last but not least a big thanks to all our [contributors](https://github.com/alexhiggins732/IdentityServer8/graphs/contributors)! ================================================ FILE: SECURITY.MD ================================================ # Reporting Security Issues If you discover a security issue in IdentityServer, please report it by sending an email to contact@identityserver8.io This will allow us to assess the risk, and make a fix available before we add a bug report to the GitHub repository. Thanks! ================================================ FILE: SPONSORS.md ================================================ # Sponsors We thank those who [support](https://www.patreon.com/identityserver) IdentityServer! ## Corporate ### Platinum [Udelt](https://udelt.no/) [Microsoft .NET](https://github.com/dotnet-at-microsoft) ### Gold [Ritter Insurance Marketing](https://www.ritterim.com) ([@RitterIM](https://twitter.com/ritterim)) [ExtranetUserManager](https://www.extranetusermanager.com) ([@eumgr](https://twitter.com/eumgr)) [Knab](https://www.knab.nl/) ([@knab_nl](https://twitter.com/knab_nl)) ### Silver Jacobus Roos Soluto ([@SolutoEng](https://twitter.com/SolutoEng)) Steinar Noem Effectory ([@effectory](https://twitter.com/effectory)) Real Page ([@RealPage](https://twitter.com/RealPage)) Justify ([@justify_legal](https://twitter.com/justify_legal)) ## Individuals Khalid Abuhakmeh ([@buhakmeh](https://twitter.com/buhakmeh)) Rasika Weliwita ([@rasikaweliwita](https://twitter.com/rasikaweliwita)) Arun David Shelly Tobias Höft ([@tobiashoeft](https://twitter.com/tobiashoeft)) William Grow James Roberts Chris Simmons ([@netchrisdotcom](https://twitter.com/netchrisdotcom)) Shawn Wildermuth Johan Boström ([@zarx](https://twitter.com/zarx)) Albert ([@DerAlbert](https://twitter.com/DerAlbert)) Hugo Biarge ([@hbiarge](https://twitter.com/hbiarge)) Ibrahim Šuta ([@ibrahimsuta](https://twitter.com/ibrahimsuta)) VIJAYA PAL NALLALA Martijn Boland Giuseppe Turitto Mauricio Schneider Norman L Covington Ryan Mendoza ([@elryry](https://twitter.com/elryry)) Colin Blair Erik Gulbrandsen Olga Klimova Alexandru Puiu Michael Calasanz Fredrik Karlsson ([@fredrik_zenit](https://twitter.com/fredrik_zenit)) Jeremy Sinclair ([@sinclairinator](https://twitter.com/sinclairinator)) Bruno Brito James Hough vanbukin ================================================ FILE: docker-compose.dcproj ================================================ 2.1 Linux f817047f-018d-4f93-bda5-58602073b634 None {Scheme}://localhost:{ServicePort} IdentityServer8.Admin docker-compose.yml docker-compose.yml docker-compose.yml ================================================ FILE: docker-compose.override.yml ================================================ version: '3.4' services: IdentityServer8.Admin: environment: - ASPNETCORE_ENVIRONMENT=Development volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro IdentityServer8.Admin.api: environment: - ASPNETCORE_ENVIRONMENT=Development volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro higginssoft.identityserver4.sts.identity: environment: - ASPNETCORE_ENVIRONMENT=Development volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro ================================================ FILE: docker-compose.vs.debug.yml ================================================ version: '3.4' services: IdentityServer8.Admin: volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro labels: com.microsoft.visualstudio.debuggee.arguments: ' --additionalProbingPath /root/.nuget/packages --additionalProbingPath /root/.nuget/fallbackpackages "bin/Debug/net6.0/IdentityServer8.Admin.dll" /seed' IdentityServer8.Admin.api: volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro higginssoft.identityserver4.sts.identity: volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro ================================================ FILE: docker-compose.vs.release.yml ================================================ version: '3.4' services: IdentityServer8.Admin: volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro labels: com.microsoft.visualstudio.debuggee.arguments: ' --additionalProbingPath /root/.nuget/packages --additionalProbingPath /root/.nuget/fallbackpackages "bin/Debug/net6.0/IdentityServer8.Admin.dll" /seed' IdentityServer8.Admin.api: volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro higginssoft.identityserver4.sts.identity: volumes: - ${APPDATA}/Microsoft/UserSecrets:/root/.microsoft/usersecrets:ro ================================================ FILE: docker-compose.yml ================================================ version: '3.4' services: nginx-proxy: image: jwilder/nginx-proxy container_name: nginx ports: - '80:80' - '443:443' volumes: - '/var/run/docker.sock:/tmp/docker.sock:ro' - './shared/nginx/vhost.d:/etc/nginx/vhost.d' - './shared/nginx/certs:/etc/nginx/certs:ro' networks: proxy: null identityserverui: aliases: - sts.higginssoft.local - admin.higginssoft.local - admin-api.higginssoft.local restart: always IdentityServer8.Admin: image: '${DOCKER_REGISTRY-}higginssoft-identityserver4-admin' build: context: . dockerfile: src/IdentityServer8.Admin/Dockerfile container_name: higginssoft-identityserver4-admin environment: - VIRTUAL_HOST=admin.higginssoft.local - 'ConnectionStrings__ConfigurationDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__PersistedGrantDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__IdentityDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__AdminLogDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__AdminAuditLogDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__DataProtectionDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'AdminConfiguration__IdentityAdminBaseUrl=https://admin.higginssoft.local' - 'AdminConfiguration__IdentityAdminRedirectUri=https://admin.higginssoft.local/signin-oidc' - 'AdminConfiguration__IdentityServerBaseUrl=https://sts.higginssoft.local' - AdminConfiguration__RequireHttpsMetadata=false - 'IdentityServerData__Clients__0__ClientUri=https://admin.higginssoft.local' - 'IdentityServerData__Clients__0__RedirectUris__0=https://admin.higginssoft.local/signin-oidc' - 'IdentityServerData__Clients__0__FrontChannelLogoutUri=https://admin.higginssoft.local/signin-oidc' - 'IdentityServerData__Clients__0__PostLogoutRedirectUris__0=https://admin.higginssoft.local/signout-callback-oidc' - 'IdentityServerData__Clients__0__AllowedCorsOrigins__0=https://admin.higginssoft.local' - 'IdentityServerData__Clients__1__RedirectUris__0=https://admin-api.higginssoft.local/swagger/oauth2-redirect.html' - 'Serilog__WriteTo__1__Args__connectionString=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - DockerConfiguration__UpdateCaCertificate=true - ASPNETCORE_ENVIRONMENT=Development command: dotnet IdentityServer8.Admin.dll /seed depends_on: - db - higginssoft.identityserver4.sts.identity volumes: - './shared/serilog.json:/app/serilog.json' - './shared/identitydata.json:/app/identitydata.json' - './shared/identityserverdata.json:/app/identityserverdata.json' - './shared/nginx/certs/cacerts.crt:/usr/local/share/ca-certificates/cacerts.crt' networks: identityserverui: null IdentityServer8.Admin.api: image: '${DOCKER_REGISTRY-}higginssoft-identityserver4-admin-api' build: context: . dockerfile: src/IdentityServer8.Admin.Api/Dockerfile container_name: higginssoft-identityserver4-admin-api environment: - VIRTUAL_HOST=admin-api.higginssoft.local - AdminApiConfiguration__RequireHttpsMetadata=false - 'AdminApiConfiguration__ApiBaseUrl=https://admin-api.higginssoft.local' - 'AdminApiConfiguration__IdentityServerBaseUrl=https://sts.higginssoft.local' - 'ConnectionStrings__ConfigurationDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__PersistedGrantDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__IdentityDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__AdminLogDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__AdminAuditLogDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__DataProtectionDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - DockerConfiguration__UpdateCaCertificate=true - ASPNETCORE_ENVIRONMENT=Development volumes: - './shared/serilog.json:/app/serilog.json' - './shared/nginx/certs/cacerts.crt:/usr/local/share/ca-certificates/cacerts.crt' networks: identityserverui: null higginssoft.identityserver4.sts.identity: image: '${DOCKER_REGISTRY-}higginssoft-identityserver4-sts-identity' build: context: . dockerfile: src/IdentityServer8.STS.Identity/Dockerfile container_name: higginssoft-identityserver4-sts-identity environment: - VIRTUAL_HOST=sts.higginssoft.local - 'ConnectionStrings__ConfigurationDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__PersistedGrantDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__IdentityDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'ConnectionStrings__DataProtectionDbConnection=Server=db;Database=IdentityServer8Admin;User Id=sa;Password=${DB_PASSWORD:-Password_123};MultipleActiveResultSets=true' - 'AdminConfiguration__IdentityAdminBaseUrl=https://admin.higginssoft.local' - 'IdentityServerOptions__IssuerUri=https://sts.higginssoft.local' - IdentityServerOptions__Events__RaiseErrorEvents=true - IdentityServerOptions__Events__RaiseInformationEvents=true - IdentityServerOptions__Events__RaiseFailureEvents=true - IdentityServerOptions__Events__RaiseSuccessEvents=true - DockerConfiguration__UpdateCaCertificate=true - ASPNETCORE_ENVIRONMENT=Development depends_on: - db volumes: - './shared/serilog.json:/app/serilog.json' - './shared/nginx/certs/cacerts.crt:/usr/local/share/ca-certificates/cacerts.crt' networks: identityserverui: aliases: - sts.higginssoft.local db: image: 'mcr.microsoft.com/mssql/server:2017-CU20-ubuntu-16.04' ports: - '7900:1433' container_name: higginssoft-identityserver4-db environment: SA_PASSWORD: '${DB_PASSWORD:-Password_123}' ACCEPT_EULA: 'Y' volumes: - 'dbdata:/var/opt/mssql' networks: identityserverui: null volumes: dbdata: driver: local networks: proxy: driver: bridge identityserverui: driver: bridge ================================================ FILE: docs/CHANGELOG.md ================================================ # Change Log All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) and this project adheres to [Semantic Versioning 2](http://semver.org/). ## [Unreleased] - 2024-02-17 - Current templates and quickstarts being added to seperate template and quickstart repositories to continue previous version functionality. - DotNet tool to install template currently under development. - ## [8.0.4] - 2024-02-17 Identity Server 8.0.4 is a security release that addresses hundreds of security vulnerabilities in the IdentityServer8 code base. We recommend that you update to this version. - Fix over 100+ security vulnerabilities in the IdentityServer8 code base: - #17 Unsafe expansion of self-closing HTML tag - #18 URL redirection from remote source - #19 DOM text reinterpreted as HTML - #20 Incomplete string escaping or encoding - #21 Inefficient regular expression bug dependencies - #22 Bad HTML filtering regexp bug dependencies - #23 User-controlled bypass of sensitive method bug - #24 Unsafe jQuery plugins bug dependencies Additional the codebase has been refactored to use the latest DotNet 8 features and best practices. This includes refactroing in #25 and consolidation of reused code that remove some nearly 1 million lines of code from the base.: - Convert Top Level usings - Convert Implicit usings. - Samples use shared API and MVC projects to reduce code duplication and need to maintain dozens of copies of the same code. ## [8.0.3] - 2024-02-12 - Security Updates: Addtional priority critical security patches addressing issues outline in #9 and #10. - [Security: User-controlled bypass of sensitive method] - Login Controller and view have have explicit methods to handle login and cancel to address User-controlled bypass of sensitive method - [Security: Logging of user-controlled data] - Unsanitized user input could be used to forge logs and inject arbitrary commands, including server side includes, xss and sql injection into log files. - [Maitenance]: Removed over half a million lines of code from the orginal Identity Server 4 code base using packages and libaries. - This will allow for easier maintenance and updates to the code base. - Developrs can now focus on the core functionality of Identity Server 8 and use LibMan to manage client side packages and keep packages up to date. - Documentation Website: identityserver8.readthedocs.io has been created and is now the official documentation website for IdentityServer8 - Gitter: A Gitter chat room has been created for IdentityServer8. You can join the chat at https://app.gitter.im/#/room/#identityserver8:gitter.im - Framework Upgrade: Upgrade Samples, including Clients, Quickstarts, and Key Management, to use DotNet 8 sdk style. - [Quickstarts] (https://github.com/alexhiggins732/IdentityServer8/tree/master/samples/Quickstarts) - Updated Quickstart samples to use Dotnet 8 startup with implicit usings and minimal Api. - [Clients] (https://github.com/alexhiggins732/IdentityServer8/tree/master/samples/Clients) - Updated client samples to use Dotnet 8 startup with implicit usings and minimal Api. - [Key Management] (https://github.com/alexhiggins732/IdentityServer8/tree/master/samples/KeyManagement) - Updated Key management samples to use Dotnet 8 startup with implicit usings and minimal Api. Changed default Entity Framework storage to file system storage as original Key Management is a paid solution. Roadmap: Add DbContext implementation fof key management. - Client Side Packages: Client Side packages have now been ignored in source and are now installed using LibMan during the build process. This will allow for easier updates and management of client side packages. ## [8.0.2] - 2024-02-12 - Security Updates: Addtional priority critical security patches addressing issues outline in #9 and #10. ## [8.0.1] - 2024-02-10 - Security Update: High priority critical security patches addressing issues outline in #9 and #10. ### Added - `IdentityServer8.Security` nuget packages with services to sanitize user input including html, json, xml, javascript, scripts, urls, logs, css, and style sheets. ### Changed - [Account Login Controller] (https://github.com/alexhiggins732/IdentityServer8/issues/9) - [Account Login View] (https://github.com/alexhiggins732/IdentityServer8/issues/9) ### Fixed - [Security: User-controlled bypass of sensitive method] Login Controller and view have have explicit methods to handle login and cancel to address User-controlled bypass of sensitive method - [Security: Logging of user-controlled data] Unsanitized user input could be used to forge logs and inject arbitrary commands, including server side includes, xss and sql injection into log files. ## [8.0.1] - 2024-02-10 Updated build scripts to use Git Flow branching for SemVer2 compatible nuget packages. ### Added - CodeQl Security scanning - Dependabot Package scanning. ### Changed - [IdentityServer8 8.0.1 changes]https://github.com/alexhiggins732/IdentityServer8/pull/7) ### Fixed - Nuget Package version conflicts. ## [8.0.0] - 2024-02-09 ### Added Build scripts and readme documentation for initial port from Identity Server 4 and Identity Server 4 Admin ### Changed Upgraded Main Identity Server projects and Nuget packages to DotNet 8 ### Fixed - Changed mixed dependencies on `System.Text.Json` and `Newtonsoft.Json` to use `System.Text.Json` which resolved several bugs. - Change package dependencies and version requirements to run on the latest DotNet 8 packages, resolving many security vulnerablities. ================================================ FILE: docs/Makefile ================================================ # Makefile for Sphinx documentation # # You can set these variables from the command line. SPHINXOPTS = SPHINXBUILD = sphinx-build PAPER = BUILDDIR = _build # Internal variables. PAPEROPT_a4 = -D latex_paper_size=a4 PAPEROPT_letter = -D latex_paper_size=letter ALLSPHINXOPTS = -d $(BUILDDIR)/doctrees $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . # the i18n builder cannot share the environment and doctrees with the others I18NSPHINXOPTS = $(PAPEROPT_$(PAPER)) $(SPHINXOPTS) . .PHONY: help help: @echo "Please use \`make ' where is one of" @echo " html to make standalone HTML files" @echo " dirhtml to make HTML files named index.html in directories" @echo " singlehtml to make a single large HTML file" @echo " pickle to make pickle files" @echo " json to make JSON files" @echo " htmlhelp to make HTML files and a HTML help project" @echo " qthelp to make HTML files and a qthelp project" @echo " applehelp to make an Apple Help Book" @echo " devhelp to make HTML files and a Devhelp project" @echo " epub to make an epub" @echo " epub3 to make an epub3" @echo " latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter" @echo " latexpdf to make LaTeX files and run them through pdflatex" @echo " latexpdfja to make LaTeX files and run them through platex/dvipdfmx" @echo " text to make text files" @echo " man to make manual pages" @echo " texinfo to make Texinfo files" @echo " info to make Texinfo files and run them through makeinfo" @echo " gettext to make PO message catalogs" @echo " changes to make an overview of all changed/added/deprecated items" @echo " xml to make Docutils-native XML files" @echo " pseudoxml to make pseudoxml-XML files for display purposes" @echo " linkcheck to check all external links for integrity" @echo " doctest to run all doctests embedded in the documentation (if enabled)" @echo " coverage to run coverage check of the documentation (if enabled)" @echo " dummy to check syntax errors of document sources" .PHONY: clean clean: rm -rf $(BUILDDIR)/* .PHONY: html html: $(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(BUILDDIR)/html @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/html." .PHONY: dirhtml dirhtml: $(SPHINXBUILD) -b dirhtml $(ALLSPHINXOPTS) $(BUILDDIR)/dirhtml @echo @echo "Build finished. The HTML pages are in $(BUILDDIR)/dirhtml." .PHONY: singlehtml singlehtml: $(SPHINXBUILD) -b singlehtml $(ALLSPHINXOPTS) $(BUILDDIR)/singlehtml @echo @echo "Build finished. The HTML page is in $(BUILDDIR)/singlehtml." .PHONY: pickle pickle: $(SPHINXBUILD) -b pickle $(ALLSPHINXOPTS) $(BUILDDIR)/pickle @echo @echo "Build finished; now you can process the pickle files." .PHONY: json json: $(SPHINXBUILD) -b json $(ALLSPHINXOPTS) $(BUILDDIR)/json @echo @echo "Build finished; now you can process the JSON files." .PHONY: htmlhelp htmlhelp: $(SPHINXBUILD) -b htmlhelp $(ALLSPHINXOPTS) $(BUILDDIR)/htmlhelp @echo @echo "Build finished; now you can run HTML Help Workshop with the" \ ".hhp project file in $(BUILDDIR)/htmlhelp." .PHONY: qthelp qthelp: $(SPHINXBUILD) -b qthelp $(ALLSPHINXOPTS) $(BUILDDIR)/qthelp @echo @echo "Build finished; now you can run "qcollectiongenerator" with the" \ ".qhcp project file in $(BUILDDIR)/qthelp, like this:" @echo "# qcollectiongenerator $(BUILDDIR)/qthelp/IdentityServer8.qhcp" @echo "To view the help file:" @echo "# assistant -collectionFile $(BUILDDIR)/qthelp/IdentityServer8.qhc" .PHONY: applehelp applehelp: $(SPHINXBUILD) -b applehelp $(ALLSPHINXOPTS) $(BUILDDIR)/applehelp @echo @echo "Build finished. The help book is in $(BUILDDIR)/applehelp." @echo "N.B. You won't be able to view it unless you put it in" \ "~/Library/Documentation/Help or install it in your application" \ "bundle." .PHONY: devhelp devhelp: $(SPHINXBUILD) -b devhelp $(ALLSPHINXOPTS) $(BUILDDIR)/devhelp @echo @echo "Build finished." @echo "To view the help file:" @echo "# mkdir -p $$HOME/.local/share/devhelp/IdentityServer8" @echo "# ln -s $(BUILDDIR)/devhelp $$HOME/.local/share/devhelp/IdentityServer8" @echo "# devhelp" .PHONY: epub epub: $(SPHINXBUILD) -b epub $(ALLSPHINXOPTS) $(BUILDDIR)/epub @echo @echo "Build finished. The epub file is in $(BUILDDIR)/epub." .PHONY: epub3 epub3: $(SPHINXBUILD) -b epub3 $(ALLSPHINXOPTS) $(BUILDDIR)/epub3 @echo @echo "Build finished. The epub3 file is in $(BUILDDIR)/epub3." .PHONY: latex latex: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo @echo "Build finished; the LaTeX files are in $(BUILDDIR)/latex." @echo "Run \`make' in that directory to run these through (pdf)latex" \ "(use \`make latexpdf' here to do that automatically)." .PHONY: latexpdf latexpdf: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through pdflatex..." $(MAKE) -C $(BUILDDIR)/latex all-pdf @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: latexpdfja latexpdfja: $(SPHINXBUILD) -b latex $(ALLSPHINXOPTS) $(BUILDDIR)/latex @echo "Running LaTeX files through platex and dvipdfmx..." $(MAKE) -C $(BUILDDIR)/latex all-pdf-ja @echo "pdflatex finished; the PDF files are in $(BUILDDIR)/latex." .PHONY: text text: $(SPHINXBUILD) -b text $(ALLSPHINXOPTS) $(BUILDDIR)/text @echo @echo "Build finished. The text files are in $(BUILDDIR)/text." .PHONY: man man: $(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(BUILDDIR)/man @echo @echo "Build finished. The manual pages are in $(BUILDDIR)/man." .PHONY: texinfo texinfo: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo @echo "Build finished. The Texinfo files are in $(BUILDDIR)/texinfo." @echo "Run \`make' in that directory to run these through makeinfo" \ "(use \`make info' here to do that automatically)." .PHONY: info info: $(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(BUILDDIR)/texinfo @echo "Running Texinfo files through makeinfo..." make -C $(BUILDDIR)/texinfo info @echo "makeinfo finished; the Info files are in $(BUILDDIR)/texinfo." .PHONY: gettext gettext: $(SPHINXBUILD) -b gettext $(I18NSPHINXOPTS) $(BUILDDIR)/locale @echo @echo "Build finished. The message catalogs are in $(BUILDDIR)/locale." .PHONY: changes changes: $(SPHINXBUILD) -b changes $(ALLSPHINXOPTS) $(BUILDDIR)/changes @echo @echo "The overview file is in $(BUILDDIR)/changes." .PHONY: linkcheck linkcheck: $(SPHINXBUILD) -b linkcheck $(ALLSPHINXOPTS) $(BUILDDIR)/linkcheck @echo @echo "Link check complete; look for any errors in the above output " \ "or in $(BUILDDIR)/linkcheck/output.txt." .PHONY: doctest doctest: $(SPHINXBUILD) -b doctest $(ALLSPHINXOPTS) $(BUILDDIR)/doctest @echo "Testing of doctests in the sources finished, look at the " \ "results in $(BUILDDIR)/doctest/output.txt." .PHONY: coverage coverage: $(SPHINXBUILD) -b coverage $(ALLSPHINXOPTS) $(BUILDDIR)/coverage @echo "Testing of coverage in the sources finished, look at the " \ "results in $(BUILDDIR)/coverage/python.txt." .PHONY: xml xml: $(SPHINXBUILD) -b xml $(ALLSPHINXOPTS) $(BUILDDIR)/xml @echo @echo "Build finished. The XML files are in $(BUILDDIR)/xml." .PHONY: pseudoxml pseudoxml: $(SPHINXBUILD) -b pseudoxml $(ALLSPHINXOPTS) $(BUILDDIR)/pseudoxml @echo @echo "Build finished. The pseudo-XML files are in $(BUILDDIR)/pseudoxml." .PHONY: dummy dummy: $(SPHINXBUILD) -b dummy $(ALLSPHINXOPTS) $(BUILDDIR)/dummy @echo @echo "Build finished. Dummy builder generates no files." ================================================ FILE: docs/autobuild.bat ================================================ sphinx-autobuild.exe . .\_build\html\ ================================================ FILE: docs/build-documentation.ps1 ================================================ .\docker-build.ps1 docker run --rm -v .:/docs sphinx-doc/sphinx_rtd_theme make html ================================================ FILE: docs/conf.py ================================================ #!/usr/bin/env python3 # -*- coding: utf-8 -*- # # IdentityServer8 documentation build configuration file, created by # sphinx-quickstart on Wed Jul 20 08:57:27 2016. # # This file is execfile()d with the current directory set to its # containing dir. # # Note that not all possible configuration values are present in this # autogenerated file. # # All configuration values have a default; values that are commented out # serve to show the default. # If extensions (or modules to document with autodoc) are in another directory, # add these directories to sys.path here. If the directory is relative to the # documentation root, use os.path.abspath to make it absolute, like shown here. # # import os # import sys # sys.path.insert(0, os.path.abspath('.')) # -- General configuration ------------------------------------------------ # If your documentation needs a minimal Sphinx version, state it here. # # needs_sphinx = '1.0' # Add any Sphinx extension module names here, as strings. They can be # extensions coming with Sphinx (named 'sphinx.ext.*') or your custom # ones. extensions = [ 'sphinx_rtd_theme', ] # Add any paths that contain templates here, relative to this directory. templates_path = ['_templates'] # The suffix(es) of source filenames. # You can specify multiple suffix as a list of string: # # source_suffix = ['.rst', '.md'] # markdown support #from recommonmark.parser import CommonMarkParser #source_parsers = { # '.md': CommonMarkParser, #} source_suffix = ['.rst'] # The encoding of source files. # # source_encoding = 'utf-8-sig' # The master toctree document. master_doc = 'index' # General information about the project. project = 'IdentityServer8' copyright = '2024 HigginsSoft, Alexander Higgins' author = 'Alexander Higgins' # The version info for the project you're documenting, acts as replacement for # |version| and |release|, also used in various other places throughout the # built documents. # # The short X.Y version. version = '8.0.0' # The full version, including alpha/beta/rc tags. release = '8.0.4' # The language for content autogenerated by Sphinx. Refer to documentation # for a list of supported languages. # # This is also used if you do content translation via gettext catalogs. # Usually you set "language" from the command line for these cases. language = None # There are two options for replacing |today|: either, you set today to some # non-false value, then it is used: # # today = '' # # Else, today_fmt is used as the format for a strftime call. # # today_fmt = '%B %d, %Y' # List of patterns, relative to source directory, that match files and # directories to ignore when looking for source files. # This patterns also effect to html_static_path and html_extra_path exclude_patterns = ['_build', 'Thumbs.db', '.DS_Store'] # The reST default role (used for this markup: `text`) to use for all # documents. # # default_role = None # If true, '()' will be appended to :func: etc. cross-reference text. # # add_function_parentheses = True # If true, the current module name will be prepended to all description # unit titles (such as .. function::). # # add_module_names = True # If true, sectionauthor and moduleauthor directives will be shown in the # output. They are ignored by default. # # show_authors = False # The name of the Pygments (syntax highlighting) style to use. pygments_style = 'default' highlight_language = 'csharp' # A list of ignored prefixes for module index sorting. # modindex_common_prefix = [] # If true, keep warnings as "system message" paragraphs in the built documents. # keep_warnings = False # If true, `todo` and `todoList` produce output, else they produce nothing. todo_include_todos = False # -- Options for HTML output ---------------------------------------------- # The theme to use for HTML and HTML Help pages. See the documentation for # a list of builtin themes. # html_theme = 'sphinx_rtd_theme' # on_rtd is whether we are on readthedocs.org, this line of code grabbed from docs.readthedocs.org import os on_rtd = os.environ.get('READTHEDOCS', None) == 'True' if not on_rtd: # only import and set the theme if we're building docs locally # import sphinx_rtd_theme html_theme = 'sphinx_rtd_theme' # html_theme_path = [sphinx_rtd_theme.get_html_theme_path()] # otherwise, readthedocs.org uses their theme by default, so no need to specify it # Theme options are theme-specific and customize the look and feel of a theme # further. For a list of options available for each theme, see the # documentation. # # html_theme_options = {} # Add any paths that contain custom themes here, relative to this directory. # html_theme_path = [] # The name for this set of Sphinx documents. # " v documentation" by default. # # html_title = 'IdentityServer8 v1.0.0' # A shorter title for the navigation bar. Default is the same as html_title. # # html_short_title = None # The name of an image file (relative to this directory) to place at the top # of the sidebar. # # html_logo = None # The name of an image file (relative to this directory) to use as a favicon of # the docs. This file should be a Windows icon file (.ico) being 16x16 or 32x32 # pixels large. # html_favicon = 'favicon.ico' # Add any paths that contain custom static files (such as style sheets) here, # relative to this directory. They are copied after the builtin static files, # so a file named "default.css" will overwrite the builtin "default.css". html_static_path = ['_static'] # Add any extra paths that contain custom files (such as robots.txt or # .htaccess) here, relative to this directory. These files are copied # directly to the root of the documentation. # # html_extra_path = [] # If not None, a 'Last updated on:' timestamp is inserted at every page # bottom, using the given strftime format. # The empty string is equivalent to '%b %d, %Y'. # # html_last_updated_fmt = None # If true, SmartyPants will be used to convert quotes and dashes to # typographically correct entities. # # html_use_smartypants = True # Custom sidebar templates, maps document names to template names. # # html_sidebars = {} # Additional templates that should be rendered to pages, maps page names to # template names. # # html_additional_pages = {} # If false, no module index is generated. # # html_domain_indices = True # If false, no index is generated. # # html_use_index = True # If true, the index is split into individual pages for each letter. # # html_split_index = False # If true, links to the reST sources are added to the pages. # # html_show_sourcelink = True # If true, "Created using Sphinx" is shown in the HTML footer. Default is True. # # html_show_sphinx = True # If true, "(C) Copyright ..." is shown in the HTML footer. Default is True. # # html_show_copyright = True # If true, an OpenSearch description file will be output, and all pages will # contain a tag referring to it. The value of this option must be the # base URL from which the finished HTML is served. # # html_use_opensearch = '' # This is the file name suffix for HTML files (e.g. ".xhtml"). # html_file_suffix = None # Language to be used for generating the HTML full-text search index. # Sphinx supports the following languages: # 'da', 'de', 'en', 'es', 'fi', 'fr', 'h', 'it', 'ja' # 'nl', 'no', 'pt', 'ro', 'r', 'sv', 'tr', 'zh' # # html_search_language = 'en' # A dictionary with options for the search language support, empty by default. # 'ja' uses this config value. # 'zh' user can custom change `jieba` dictionary path. # # html_search_options = {'type': 'default'} # The name of a javascript file (relative to the configuration directory) that # implements a search results scorer. If empty, the default will be used. # # html_search_scorer = 'scorer.js' # Output file base name for HTML help builder. htmlhelp_basename = 'IdentityServer8doc' # -- Options for LaTeX output --------------------------------------------- latex_elements = { # The paper size ('letterpaper' or 'a4paper'). # # 'papersize': 'letterpaper', # The font size ('10pt', '11pt' or '12pt'). # # 'pointsize': '10pt', # Additional stuff for the LaTeX preamble. # # 'preamble': '', # Latex figure (float) alignment # # 'figure_align': 'htbp', } # Grouping the document tree into LaTeX files. List of tuples # (source start file, target name, title, # author, documentclass [howto, manual, or own class]). latex_documents = [ (master_doc, 'IdentityServer8.tex', 'IdentityServer8 Documentation', 'HigginsSoft, Alexander Higgins', 'manual'), ] # The name of an image file (relative to this directory) to place at the top of # the title page. # # latex_logo = None # For "manual" documents, if this is true, then toplevel headings are parts, # not chapters. # # latex_use_parts = False # If true, show page references after internal links. # # latex_show_pagerefs = False # If true, show URL addresses after external links. # # latex_show_urls = False # Documents to append as an appendix to all manuals. # # latex_appendices = [] # It false, will not define \strong, \code, itleref, \crossref ... but only # \sphinxstrong, ..., \sphinxtitleref, ... To help avoid clash with user added # packages. # # latex_keep_old_macro_names = True # If false, no module index is generated. # # latex_domain_indices = True # -- Options for manual page output --------------------------------------- # One entry per manual page. List of tuples # (source start file, name, description, authors, manual section). man_pages = [ (master_doc, 'IdentityServer8', 'IdentityServer8 Documentation', [author], 1) ] # If true, show URL addresses after external links. # # man_show_urls = False # -- Options for Texinfo output ------------------------------------------- # Grouping the document tree into Texinfo files. List of tuples # (source start file, target name, title, author, # dir menu entry, description, category) texinfo_documents = [ (master_doc, 'IdentityServer8', 'IdentityServer8 Documentation', author, 'IdentityServer8', 'One line description of project.', 'Miscellaneous'), ] # Documents to append as an appendix to all manuals. # # texinfo_appendices = [] # If false, no module index is generated. # # texinfo_domain_indices = True # How to display URL addresses: 'footnote', 'no', or 'inline'. # # texinfo_show_urls = 'footnote' # If true, do not generate a @detailmenu in the "Top" node's menu. # # texinfo_no_detailmenu = False ================================================ FILE: docs/docker-build.ps1 ================================================ docker build -t sphinx-doc/sphinx_rtd_theme . ================================================ FILE: docs/dockerfile ================================================ FROM sphinxdoc/sphinx RUN pip install sphinx_rtd_theme ================================================ FILE: docs/endpoints/authorize.rst ================================================ Authorize Endpoint ================== The authorize endpoint can be used to request tokens or authorization codes via the browser. This process typically involves authentication of the end-user and optionally consent. .. Note:: IdentityServer supports a subset of the OpenID Connect and OAuth 2.0 authorize request parameters. For a full list, see `here `_. ``client_id`` identifier of the client (required). ``request`` instead of providing all parameters as individual query string parameters, you can provide a subset or all of them as a JWT ``request_uri`` URL of a pre-packaged JWT containing request parameters ``scope`` one or more registered scopes (required) ``redirect_uri`` must exactly match one of the allowed redirect URIs for that client (required) ``response_type`` ``id_token`` requests an identity token (only identity scopes are allowed) ``token`` requests an access token (only resource scopes are allowed) ``id_token token`` requests an identity token and an access token ``code`` requests an authorization code ``code id_token`` requests an authorization code and identity token ``code id_token token`` requests an authorization code, identity token and access token ``response_mode`` ``form_post`` sends the token response as a form post instead of a fragment encoded redirect (optional) ``state`` identityserver will echo back the state value on the token response, this is for round tripping state between client and provider, correlating request and response and CSRF/replay protection. (recommended) ``nonce`` identityserver will echo back the nonce value in the identity token, this is for replay protection) *Required for identity tokens via implicit grant.* ``prompt`` ``none`` no UI will be shown during the request. If this is not possible (e.g. because the user has to sign in or consent) an error is returned ``login`` the login UI will be shown, even if the user is already signed-in and has a valid session ``code_challenge`` sends the code challenge for PKCE ``code_challenge_method`` ``plain`` indicates that the challenge is using plain text (not recommended) ``S256`` indicates the challenge is hashed with SHA256 ``login_hint`` can be used to pre-fill the username field on the login page ``ui_locales`` gives a hint about the desired display language of the login UI ``max_age`` if the user's logon session exceeds the max age (in seconds), the login UI will be shown ``acr_values`` allows passing in additional authentication related information - identityserver special cases the following proprietary acr_values: ``idp:name_of_idp`` bypasses the login/home realm screen and forwards the user directly to the selected identity provider (if allowed per client configuration) ``tenant:name_of_tenant`` can be used to pass a tenant name to the login UI **Example** :: GET /connect/authorize? client_id=client1& scope=openid email api1& response_type=id_token token& redirect_uri=https://myapp/callback& state=abc& nonce=xyz (URL encoding removed, and line breaks added for readability) .. Note:: You can use the `IdentityModel `_ client library to programmatically create authorize requests .NET code. For more information check the IdentityModel `docs `_. ================================================ FILE: docs/endpoints/device_authorization.rst ================================================ Device Authorization Endpoint ============================= The device authorization endpoint can be used to request device and user codes. This endpoint is used to start the device flow authorization process. .. Note:: The URL for the end session endpoint is available via the :ref:`discovery endpoint `. ``client_id`` client identifier (required) ``client_secret`` client secret either in the post body, or as a basic authentication header. Optional. ``scope`` one or more registered scopes. If not specified, a token for all explicitly allowed scopes will be issued. Example ^^^^^^^ :: POST /connect/deviceauthorization client_id=client1& client_secret=secret& scope=openid api1 (Form-encoding removed and line breaks added for readability) .. Note:: You can use the `IdentityModel `_ client library to programmatically access the device authorization endpoint from .NET code. For more information check the IdentityModel `docs `_. ================================================ FILE: docs/endpoints/discovery.rst ================================================ .. _refDiscovery: Discovery Endpoint ================== The discovery endpoint can be used to retrieve metadata about your IdentityServer - it returns information like the issuer name, key material, supported scopes etc. See the `spec `_ for more details. The discovery endpoint is available via `/.well-known/openid-configuration` relative to the base address, e.g.:: https://demo.identityserver8.io/.well-known/openid-configuration .. Note:: You can use the `IdentityModel `_ client library to programmatically access the discovery endpoint from .NET code. For more information check the IdentityModel `docs `_. ================================================ FILE: docs/endpoints/endsession.rst ================================================ .. _refEndSession: End Session Endpoint ==================== The end session endpoint can be used to trigger single sign-out (see `spec `_). To use the end session endpoint a client application will redirect the user's browser to the end session URL. All applications that the user has logged into via the browser during the user's session can participate in the sign-out. .. Note:: The URL for the end session endpoint is available via the :ref:`discovery endpoint `. Parameters ^^^^^^^^^^ **id_token_hint** When the user is redirected to the endpoint, they will be prompted if they really want to sign-out. This prompt can be bypassed by a client sending the original *id_token* received from authentication. This is passed as a query string parameter called ``id_token_hint``. **post_logout_redirect_uri** If a valid ``id_token_hint`` is passed, then the client may also send a ``post_logout_redirect_uri`` parameter. This can be used to allow the user to redirect back to the client after sign-out. The value must match one of the client's pre-configured `PostLogoutRedirectUris` (:ref:`client docs `). **state** If a valid ``post_logout_redirect_uri`` is passed, then the client may also send a ``state`` parameter. This will be returned back to the client as a query string parameter after the user redirects back to the client. This is typically used by clients to round-trip state across the redirect. Example ^^^^^^^ :: GET /connect/endsession?id_token_hint=eyJhbGciOiJSUzI1NiIsImtpZCI6IjdlOGFkZmMzMjU1OTEyNzI0ZDY4NWZmYmIwOThjNDEyIiwidHlwIjoiSldUIn0.eyJuYmYiOjE0OTE3NjUzMjEsImV4cCI6MTQ5MTc2NTYyMSwiaXNzIjoiaHR0cDovL2xvY2FsaG9zdDo1MDAwIiwiYXVkIjoianNfb2lkYyIsIm5vbmNlIjoiYTQwNGFjN2NjYWEwNGFmNzkzNmJjYTkyNTJkYTRhODUiLCJpYXQiOjE0OTE3NjUzMjEsInNpZCI6IjI2YTYzNWVmOTQ2ZjRiZGU3ZWUzMzQ2ZjFmMWY1NTZjIiwic3ViIjoiODg0MjExMTMiLCJhdXRoX3RpbWUiOjE0OTE3NjUzMTksImlkcCI6ImxvY2FsIiwiYW1yIjpbInB3ZCJdfQ.STzOWoeVYMtZdRAeRT95cMYEmClixWkmGwVH2Yyiks9BETotbSZiSfgE5kRh72kghN78N3-RgCTUmM2edB3bZx4H5ut3wWsBnZtQ2JLfhTwJAjaLE9Ykt68ovNJySbm8hjZhHzPWKh55jzshivQvTX0GdtlbcDoEA1oNONxHkpDIcr3pRoGi6YveEAFsGOeSQwzT76aId-rAALhFPkyKnVc-uB8IHtGNSyRWLFhwVqAdS3fRNO7iIs5hYRxeFSU7a5ZuUqZ6RRi-bcDhI-djKO5uAwiyhfpbpYcaY_TxXWoCmq8N8uAw9zqFsQUwcXymfOAi2UF3eFZt02hBu-shKA&post_logout_redirect_uri=http%3A%2F%2Flocalhost%3A7017%2Findex.html .. Note:: You can use the `IdentityModel `_ client library to programmatically create end_session requests .NET code. For more information check the IdentityModel `docs `_. ================================================ FILE: docs/endpoints/introspection.rst ================================================ Introspection Endpoint ====================== The introspection endpoint is an implementation of `RFC 7662 `_. It can be used to validate reference tokens (or JWTs if the consumer does not have support for appropriate JWT or cryptographic libraries). The introspection endpoint requires authentication - since the client of an introspection endpoint is an API, you configure the secret on the ``ApiResource``. Example ^^^^^^^ :: POST /connect/introspect Authorization: Basic xxxyyy token= A successful response will return a status code of 200 and either an active or inactive token:: { "active": true, "sub": "123" } Unknown or expired tokens will be marked as inactive:: { "active": false, } An invalid request will return a 400, an unauthorized request 401. .. Note:: You can use the `IdentityModel `_ client library to programmatically access the introspection endpoint from .NET code. For more information check the IdentityModel `docs `_. ================================================ FILE: docs/endpoints/revocation.rst ================================================ Revocation Endpoint =================== This endpoint allows revoking access tokens (reference tokens only) and refresh token. It implements the token revocation specification `(RFC 7009) `_. ``token`` the token to revoke (required) ``token_type_hint`` either ``access_token`` or ``refresh_token`` (optional) Example ^^^^^^^ :: POST /connect/revocation HTTP/1.1 Host: server.example.com Content-Type: application/x-www-form-urlencoded Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW token=45ghiukldjahdnhzdauz&token_type_hint=refresh_token .. Note:: You can use the `IdentityModel `_ client library to programmatically access the revocation endpoint from .NET code. For more information check the IdentityModel `docs `_. ================================================ FILE: docs/endpoints/token.rst ================================================ Token Endpoint ============== The token endpoint can be used to programmatically request tokens. It supports the ``password``, ``authorization_code``, ``client_credentials``, ``refresh_token`` and ``urn:ietf:params:oauth:grant-type:device_code`` grant types. Furthermore the token endpoint can be extended to support extension grant types. .. Note:: IdentityServer supports a subset of the OpenID Connect and OAuth 2.0 token request parameters. For a full list, see `here `_. ``client_id`` client identifier (required – Either in the body or as part of the authorization header.) ``client_secret`` client secret either in the post body, or as a basic authentication header. Optional. ``grant_type`` ``authorization_code``, ``client_credentials``, ``password``, ``refresh_token``, ``urn:ietf:params:oauth:grant-type:device_code`` or custom ``scope`` one or more registered scopes. If not specified, a token for all explicitly allowed scopes will be issued. ``redirect_uri`` required for the ``authorization_code`` grant type ``code`` the authorization code (required for ``authorization_code`` grant type) ``code_verifier`` PKCE proof key ``username`` resource owner username (required for ``password`` grant type) ``password`` resource owner password (required for ``password`` grant type) ``acr_values`` allows passing in additional authentication related information for the ``password`` grant type - identityserver special cases the following proprietary acr_values: ``idp:name_of_idp`` bypasses the login/home realm screen and forwards the user directly to the selected identity provider (if allowed per client configuration) ``tenant:name_of_tenant`` can be used to pass a tenant name to the token endpoint ``refresh_token`` the refresh token (required for ``refresh_token`` grant type) ``device_code`` the device code (required for ``urn:ietf:params:oauth:grant-type:device_code`` grant type) Example ^^^^^^^ :: POST /connect/token CONTENT-TYPE application/x-www-form-urlencoded client_id=client1& client_secret=secret& grant_type=authorization_code& code=hdh922& redirect_uri=https://myapp.com/callback (Form-encoding removed and line breaks added for readability) .. Note:: You can use the `IdentityModel `_ client library to programmatically access the token endpoint from .NET code. For more information check the IdentityModel `docs `_. ================================================ FILE: docs/endpoints/userinfo.rst ================================================ UserInfo Endpoint ================= The UserInfo endpoint can be used to retrieve identity information about a user (see `spec `_). The caller needs to send a valid access token representing the user. Depending on the granted scopes, the UserInfo endpoint will return the mapped claims (at least the `openid` scope is required). Example ^^^^^^^ :: GET /connect/userinfo Authorization: Bearer :: HTTP/1.1 200 OK Content-Type: application/json { "sub": "248289761001", "name": "Bob Smith", "given_name": "Bob", "family_name": "Smith", "role": [ "user", "admin" ] } .. Note:: You can use the `IdentityModel `_ client library to programmatically access the userinfo endpoint from .NET code. For more information check the IdentityModel `docs `_. ================================================ FILE: docs/identityserver docs figures.pptx ================================================ [File too large to display: 22.5 MB] ================================================ FILE: docs/index.rst ================================================ Welcome to IdentityServer8 (latest) ============================================= .. image:: images/logo.png :align: center IdentityServer8 is an OpenID Connect and OAuth 2.0 framework for ASP.NET DotNet 8. Browse the latest `IdentityServer8 source code onGitHub `_ or download the `latest IdentyServer8 packages `_ on NuGet. .. warning:: This is a revival of the archived IdentityServer4 project which started a new `company `_ as of Oct, 1st 2020. The new Duende IdentityServer is not longer free open source, but now has various commercial licenses and paid upgrade package. IdentityServer8 and dependenices have been upgraded to DotNet 8 and will be maintained by HigginsSoft, Alexander Higgins and the community as an Open Source project. .. note:: This docs cover the latest version on main branch. This might not be released yet. Use the version picker in the lower left corner to select docs for a specific version. It enables the following features in your applications: | **Authentication as a Service** | Centralized login logic and workflow for all of your applications (web, native, mobile, services). IdentityServer is an officially `certified `_ implementation of OpenID Connect. | **Single Sign-on / Sign-out** | Single sign-on (and out) over multiple application types. | **Access Control for APIs** | Issue access tokens for APIs for various types of clients, e.g. server to server, web applications, SPAs and native/mobile apps. | **Federation Gateway** | Support for external identity providers like Azure Active Directory, Google, Facebook etc. This shields your applications from the details of how to connect to these external providers. | **Focus on Customization** | The most important part - many aspects of IdentityServer can be customized to fit **your** needs. Since IdentityServer is a framework and not a boxed product or a SaaS, you can write code to adapt the system the way it makes sense for your scenarios. | **Mature Open Source** | IdentityServer uses the permissive `Apache 2 `_ license that allows building commercial products on top of it. It is also part of the `.NET Foundation `_ which provides governance and legal backing. | **Free and Commercial Support** | If you need help building or running your identity platform, :ref:`let us know `. There are several ways we can help you out. .. toctree:: :maxdepth: 3 :hidden: :caption: Introduction intro/big_picture intro/architecture intro/terminology intro/specs intro/packaging intro/support intro/test intro/contributing .. toctree:: :maxdepth: 3 :hidden: :caption: Quickstarts quickstarts/0_overview quickstarts/1_client_credentials quickstarts/2_interactive_aspnetcore quickstarts/3_aspnetcore_and_apis quickstarts/4_javascript_client quickstarts/5_entityframework quickstarts/6_aspnet_identity .. toctree:: :maxdepth: 3 :hidden: :caption: Configuration configuration/startup configuration/resources configuration/clients configuration/mvc configuration/apis .. toctree:: :maxdepth: 3 :hidden: :caption: Topics topics/startup topics/resources topics/clients topics/signin topics/signin_external_providers topics/windows topics/signout topics/signout_external_providers topics/signout_federated topics/federation_gateway topics/consent topics/apis topics/deployment topics/logging topics/events topics/crypto topics/grant_types topics/client_authentication topics/extension_grants topics/resource_owner topics/refresh_tokens topics/reference_tokens topics/persisted_grants topics/pop topics/mtls topics/request_object topics/custom_token_request_validation topics/cors topics/discovery topics/add_apis topics/add_protocols topics/tools .. toctree:: :maxdepth: 3 :hidden: :caption: Endpoints endpoints/discovery endpoints/authorize endpoints/token endpoints/userinfo endpoints/device_authorization endpoints/introspection endpoints/revocation endpoints/endsession .. toctree:: :maxdepth: 3 :hidden: :caption: Reference reference/options reference/identity_resource reference/api_scope reference/api_resource reference/client reference/grant_validation_result reference/profileservice reference/interactionservice reference/deviceflow_interactionservice reference/ef reference/aspnet_identity .. toctree:: :maxdepth: 3 :hidden: :caption: Misc misc/training misc/blogs misc/videos ================================================ FILE: docs/intro/big_picture.rst ================================================ The Big Picture =============== Most modern applications look more or less like this: .. image:: images/appArch.png The most common interactions are: * Browsers communicate with web applications * Web applications communicate with web APIs (sometimes on their own, sometimes on behalf of a user) * Browser-based applications communicate with web APIs * Native applications communicate with web APIs * Server-based applications communicate with web APIs * Web APIs communicate with web APIs (sometimes on their own, sometimes on behalf of a user) Typically each and every layer (front-end, middle-tier and back-end) has to protect resources and implement authentication and/or authorization – often against the same user store. Outsourcing these fundamental security functions to a security token service prevents duplicating that functionality across those applications and endpoints. Restructuring the application to support a security token service leads to the following architecture and protocols: .. image:: images/protocols.png Such a design divides security concerns into two parts: Authentication ^^^^^^^^^^^^^^ Authentication is needed when an application needs to know the identity of the current user. Typically these applications manage data on behalf of that user and need to make sure that this user can only access the data for which he is allowed. The most common example for that is (classic) web applications – but native and JS-based applications also have a need for authentication. The most common authentication protocols are SAML2p, WS-Federation and OpenID Connect – SAML2p being the most popular and the most widely deployed. OpenID Connect is the newest of the three, but is considered to be the future because it has the most potential for modern applications. It was built for mobile application scenarios right from the start and is designed to be API friendly. API Access ^^^^^^^^^^ Applications have two fundamental ways with which they communicate with APIs – using the application identity, or delegating the user’s identity. Sometimes both methods need to be combined. OAuth2 is a protocol that allows applications to request access tokens from a security token service and use them to communicate with APIs. This delegation reduces complexity in both the client applications as well as the APIs since authentication and authorization can be centralized. OpenID Connect and OAuth 2.0 – better together ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ OpenID Connect and OAuth 2.0 are very similar – in fact OpenID Connect is an extension on top of OAuth 2.0. The two fundamental security concerns, authentication and API access, are combined into a single protocol - often with a single round trip to the security token service. We believe that the combination of OpenID Connect and OAuth 2.0 is the best approach to secure modern applications for the foreseeable future. IdentityServer8 is an implementation of these two protocols and is highly optimized to solve the typical security problems of today’s mobile, native and web applications. How IdentityServer8 can help ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ IdentityServer is middleware that adds the spec compliant OpenID Connect and OAuth 2.0 endpoints to an arbitrary ASP.NET Core application. Typically, you build (or re-use) an application that contains a login and logout page (and maybe consent - depending on your needs), and the IdentityServer middleware adds the necessary protocol heads to it, so that client applications can talk to it using those standard protocols. .. image:: images/middleware.png The hosting application can be as complex as you want, but we typically recommend to keep the attack surface as small as possible by including authentication related UI only. ================================================ FILE: docs/intro/contributing.rst ================================================ Contributing ============ We are very open to community contributions, but there are a couple of guidelines you should follow so we can handle this without too much effort. How to contribute? ^^^^^^^^^^^^^^^^^^ The easiest way to contribute is to open an issue and start a discussion. Then we can decide if and how a feature or a change could be implemented. If you should submit a pull request with code changes, start with a description, only make the minimal changes to start with and provide tests that cover those changes. Also read this first: `Being a good open source citizen `_ General feedback and discussions? ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Please start a discussion on the `core repo issue tracker `_. Bugs and feature requests? ^^^^^^^^^^^^^^^^^^^^^^^^^^ Please log a new issue in the appropriate GitHub repo: * `Core `_ * `AccessTokenValidation `_ Contributing code and content ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ You will need to sign a Contributor License Agreement before you can contribute any code or content. This is an automated process that will start after you opened a pull request. Contribution projects ^^^^^^^^^^^^^^^^^^^^^ We very much appreciate if you start a contribution project (e.g. support for Database X or Configuration Store Y). Tell us about it so we can tweet and link it in our docs. We generally don't want to take ownership of those contribution libraries, we are already really busy supporting the core projects. **Naming conventions** As of October 2017, the IdentityServer8.* nuget namespace is reserved for our packages. Please use the following naming conventions: ``YourProjectName.IdentityServer8`` or ``IdentityServer8.Contrib.YourProjectName`` ================================================ FILE: docs/intro/packaging.rst ================================================ Packaging and Builds ==================== IdentityServer consists of a number of nuget packages. IdentityServer8 main repo ^^^^^^^^^^^^^^^ `github `_ Contains the core IdentityServer object model, services and middleware as well as the EntityFramework and ASP.NET Identity integration. nugets: * `HigginsSoft.IdentityServer8 `_ * `HigginsSoft.IdentityServer8.EntityFramework `_ * `HigginsSoft.IdentityServer8.AspNetIdentity `_ Quickstart UI ^^^^^^^^^^^^^ `github `_ Contains a simple starter UI including login, logout and consent pages. Access token validation handler ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `nuget `_ | `github `_ ASP.NET Core authentication handler for validating tokens in APIs. The handler allows supporting both JWT and reference tokens in the same API. Templates ^^^^^^^^^ `nuget `_ | `github `_ Contains templates for the dotnet CLI. Dev builds ^^^^^^^^^^ In addition we publish CI builds to our package repository. Add the following ``nuget.config`` to your project:: ================================================ FILE: docs/intro/specs.rst ================================================ Supported Specifications ======================== IdentityServer implements the following specifications: OpenID Connect ^^^^^^^^^^^^^^ * OpenID Connect Core 1.0 (`spec `_) * OpenID Connect Discovery 1.0 (`spec `_) * OpenID Connect RP-Initiated Logout 1.0 - draft 01 (`spec `_) * OpenID Connect Session Management 1.0 - draft 30 (`spec `_) * OpenID Connect Front-Channel Logout 1.0 - draft 04 (`spec `_) * OpenID Connect Back-Channel Logout 1.0 - draft 06 (`spec `_) OAuth 2.0 ^^^^^^^^^ * OAuth 2.0 (`RFC 6749 `_) * OAuth 2.0 Bearer Token Usage (`RFC 6750 `_) * OAuth 2.0 Multiple Response Types (`spec `_) * OAuth 2.0 Form Post Response Mode (`spec `_) * OAuth 2.0 Token Revocation (`RFC 7009 `_) * OAuth 2.0 Token Introspection (`RFC 7662 `_) * Proof Key for Code Exchange (`RFC 7636 `_) * JSON Web Tokens for Client Authentication (`RFC 7523 `_) * OAuth 2.0 Device Authorization Grant (`RFC 8628 `_) * OAuth 2.0 Mutual TLS Client Authentication and Certificate-Bound Access Tokens (`RFC 8705 `_) * JWT Secured Authorization Request (`draft `_) ================================================ FILE: docs/intro/support.rst ================================================ .. _refSupport: Support and Consulting Options ============================== We have several free and commercial support and consulting options for IdentityServer. Free support ^^^^^^^^^^^^ Free support is community-based and uses public forums **StackOverflow** There's an ever growing community of people using IdentityServer that monitor questions on StackOverflow. If time permits, we also try to answer as many questions as possible You can subscribe to all IdentityServer8 related questions using this feed: https://stackoverflow.com/questions/tagged/?tagnames=IdentityServer8&sort=newest Please use the ``IdentityServer8`` tag when asking new questions **Gitter** You can chat with other IdentityServer8 users in our Gitter chat room: https://app.gitter.im/#/room/#identityserver8:gitter.im **Reporting a bug** If you think you have found a bug or unexpected behavior, please open an issue on the Github `issue tracker `_. We try to get back to you ASAP. Please understand that we also have day jobs, and might be too busy to reply immediately. Also check the `contribution `_ guidelines before posting. Commercial support ^^^^^^^^^^^^^^^^^^ We are doing consulting, mentoring and custom software development around identity & access control architecture in general, and IdentityServer in particular. Please `get in touch `_ with us to discuss possible options. **Training** We are regularly doing workshops around identity & access control for modern applications. Check the agenda and upcoming public dates `here `_. We can also perform the training privately at your company. `Contact us `_ to request the training on-site. **AdminUI, WS-Federation, SAML2p, and FIDO2 support** There are commercial add-on products available from our partners, Rock Solid Knowledge, on `identityserver8.com `_. ================================================ FILE: docs/intro/terminology.rst ================================================ Terminology =========== The specs, documentation and object model use a certain terminology that you should be aware of. .. image:: images/terminology.png IdentityServer ^^^^^^^^^^^^^^ IdentityServer is an OpenID Connect provider - it implements the OpenID Connect and OAuth 2.0 protocols. Different literature uses different terms for the same role - you probably also find security token service, identity provider, authorization server, IP-STS and more. But they are in a nutshell all the same: a piece of software that issues security tokens to clients. IdentityServer has a number of jobs and features - including: * protect your resources * authenticate users using a local account store or via an external identity provider * provide session management and single sign-on * manage and authenticate clients * issue identity and access tokens to clients * validate tokens User ^^^^ A user is a human that is using a registered client to access resources. Client ^^^^^^ A client is a piece of software that requests tokens from IdentityServer - either for authenticating a user (requesting an identity token) or for accessing a resource (requesting an access token). A client must be first registered with IdentityServer before it can request tokens. Examples for clients are web applications, native mobile or desktop applications, SPAs, server processes etc. Resources ^^^^^^^^^ Resources are something you want to protect with IdentityServer - either identity data of your users, or APIs. Every resource has a unique name - and clients use this name to specify to which resources they want to get access to. **Identity data** Identity information (aka claims) about a user, e.g. name or email address. **APIs** APIs resources represent functionality a client wants to invoke - typically modelled as Web APIs, but not necessarily. Identity Token ^^^^^^^^^^^^^^ An identity token represents the outcome of an authentication process. It contains at a bare minimum an identifier for the user (called the `sub` aka subject claim) and information about how and when the user authenticated. It can contain additional identity data. Access Token ^^^^^^^^^^^^ An access token allows access to an API resource. Clients request access tokens and forward them to the API. Access tokens contain information about the client and the user (if present). APIs use that information to authorize access to their data. ================================================ FILE: docs/intro/test.rst ================================================ Demo Server =========== You can try IdentityServer8 with your favourite client library. We have a test instance at `demo.identityserver8.io `_. On the main page you can find instructions on how to configure your client and how to call an API. ================================================ FILE: docs/make.bat ================================================ @ECHO OFF REM Command file for Sphinx documentation if "%SPHINXBUILD%" == "" ( set SPHINXBUILD=sphinx-build ) set BUILDDIR=_build set ALLSPHINXOPTS=-d %BUILDDIR%/doctrees %SPHINXOPTS% . set I18NSPHINXOPTS=%SPHINXOPTS% . if NOT "%PAPER%" == "" ( set ALLSPHINXOPTS=-D latex_paper_size=%PAPER% %ALLSPHINXOPTS% set I18NSPHINXOPTS=-D latex_paper_size=%PAPER% %I18NSPHINXOPTS% ) if "%1" == "" goto help if "%1" == "help" ( :help echo.Please use `make ^` where ^ is one of echo. html to make standalone HTML files echo. dirhtml to make HTML files named index.html in directories echo. singlehtml to make a single large HTML file echo. pickle to make pickle files echo. json to make JSON files echo. htmlhelp to make HTML files and a HTML help project echo. qthelp to make HTML files and a qthelp project echo. devhelp to make HTML files and a Devhelp project echo. epub to make an epub echo. epub3 to make an epub3 echo. latex to make LaTeX files, you can set PAPER=a4 or PAPER=letter echo. text to make text files echo. man to make manual pages echo. texinfo to make Texinfo files echo. gettext to make PO message catalogs echo. changes to make an overview over all changed/added/deprecated items echo. xml to make Docutils-native XML files echo. pseudoxml to make pseudoxml-XML files for display purposes echo. linkcheck to check all external links for integrity echo. doctest to run all doctests embedded in the documentation if enabled echo. coverage to run coverage check of the documentation if enabled echo. dummy to check syntax errors of document sources goto end ) if "%1" == "clean" ( for /d %%i in (%BUILDDIR%\*) do rmdir /q /s %%i del /q /s %BUILDDIR%\* goto end ) REM Check if sphinx-build is available and fallback to Python version if any %SPHINXBUILD% 1>NUL 2>NUL if errorlevel 9009 goto sphinx_python goto sphinx_ok :sphinx_python set SPHINXBUILD=python -m sphinx.__init__ %SPHINXBUILD% 2> nul if errorlevel 9009 ( echo. echo.The 'sphinx-build' command was not found. Make sure you have Sphinx echo.installed, then set the SPHINXBUILD environment variable to point echo.to the full path of the 'sphinx-build' executable. Alternatively you echo.may add the Sphinx directory to PATH. echo. echo.If you don't have Sphinx installed, grab it from echo.http://sphinx-doc.org/ exit /b 1 ) :sphinx_ok if "%1" == "html" ( %SPHINXBUILD% -b html %ALLSPHINXOPTS% %BUILDDIR%/html if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/html. goto end ) if "%1" == "dirhtml" ( %SPHINXBUILD% -b dirhtml %ALLSPHINXOPTS% %BUILDDIR%/dirhtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/dirhtml. goto end ) if "%1" == "singlehtml" ( %SPHINXBUILD% -b singlehtml %ALLSPHINXOPTS% %BUILDDIR%/singlehtml if errorlevel 1 exit /b 1 echo. echo.Build finished. The HTML pages are in %BUILDDIR%/singlehtml. goto end ) if "%1" == "pickle" ( %SPHINXBUILD% -b pickle %ALLSPHINXOPTS% %BUILDDIR%/pickle if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the pickle files. goto end ) if "%1" == "json" ( %SPHINXBUILD% -b json %ALLSPHINXOPTS% %BUILDDIR%/json if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can process the JSON files. goto end ) if "%1" == "htmlhelp" ( %SPHINXBUILD% -b htmlhelp %ALLSPHINXOPTS% %BUILDDIR%/htmlhelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run HTML Help Workshop with the ^ .hhp project file in %BUILDDIR%/htmlhelp. goto end ) if "%1" == "qthelp" ( %SPHINXBUILD% -b qthelp %ALLSPHINXOPTS% %BUILDDIR%/qthelp if errorlevel 1 exit /b 1 echo. echo.Build finished; now you can run "qcollectiongenerator" with the ^ .qhcp project file in %BUILDDIR%/qthelp, like this: echo.^> qcollectiongenerator %BUILDDIR%\qthelp\IdentityServer8.qhcp echo.To view the help file: echo.^> assistant -collectionFile %BUILDDIR%\qthelp\IdentityServer8.ghc goto end ) if "%1" == "devhelp" ( %SPHINXBUILD% -b devhelp %ALLSPHINXOPTS% %BUILDDIR%/devhelp if errorlevel 1 exit /b 1 echo. echo.Build finished. goto end ) if "%1" == "epub" ( %SPHINXBUILD% -b epub %ALLSPHINXOPTS% %BUILDDIR%/epub if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub file is in %BUILDDIR%/epub. goto end ) if "%1" == "epub3" ( %SPHINXBUILD% -b epub3 %ALLSPHINXOPTS% %BUILDDIR%/epub3 if errorlevel 1 exit /b 1 echo. echo.Build finished. The epub3 file is in %BUILDDIR%/epub3. goto end ) if "%1" == "latex" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex if errorlevel 1 exit /b 1 echo. echo.Build finished; the LaTeX files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdf" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "latexpdfja" ( %SPHINXBUILD% -b latex %ALLSPHINXOPTS% %BUILDDIR%/latex cd %BUILDDIR%/latex make all-pdf-ja cd %~dp0 echo. echo.Build finished; the PDF files are in %BUILDDIR%/latex. goto end ) if "%1" == "text" ( %SPHINXBUILD% -b text %ALLSPHINXOPTS% %BUILDDIR%/text if errorlevel 1 exit /b 1 echo. echo.Build finished. The text files are in %BUILDDIR%/text. goto end ) if "%1" == "man" ( %SPHINXBUILD% -b man %ALLSPHINXOPTS% %BUILDDIR%/man if errorlevel 1 exit /b 1 echo. echo.Build finished. The manual pages are in %BUILDDIR%/man. goto end ) if "%1" == "texinfo" ( %SPHINXBUILD% -b texinfo %ALLSPHINXOPTS% %BUILDDIR%/texinfo if errorlevel 1 exit /b 1 echo. echo.Build finished. The Texinfo files are in %BUILDDIR%/texinfo. goto end ) if "%1" == "gettext" ( %SPHINXBUILD% -b gettext %I18NSPHINXOPTS% %BUILDDIR%/locale if errorlevel 1 exit /b 1 echo. echo.Build finished. The message catalogs are in %BUILDDIR%/locale. goto end ) if "%1" == "changes" ( %SPHINXBUILD% -b changes %ALLSPHINXOPTS% %BUILDDIR%/changes if errorlevel 1 exit /b 1 echo. echo.The overview file is in %BUILDDIR%/changes. goto end ) if "%1" == "linkcheck" ( %SPHINXBUILD% -b linkcheck %ALLSPHINXOPTS% %BUILDDIR%/linkcheck if errorlevel 1 exit /b 1 echo. echo.Link check complete; look for any errors in the above output ^ or in %BUILDDIR%/linkcheck/output.txt. goto end ) if "%1" == "doctest" ( %SPHINXBUILD% -b doctest %ALLSPHINXOPTS% %BUILDDIR%/doctest if errorlevel 1 exit /b 1 echo. echo.Testing of doctests in the sources finished, look at the ^ results in %BUILDDIR%/doctest/output.txt. goto end ) if "%1" == "coverage" ( %SPHINXBUILD% -b coverage %ALLSPHINXOPTS% %BUILDDIR%/coverage if errorlevel 1 exit /b 1 echo. echo.Testing of coverage in the sources finished, look at the ^ results in %BUILDDIR%/coverage/python.txt. goto end ) if "%1" == "xml" ( %SPHINXBUILD% -b xml %ALLSPHINXOPTS% %BUILDDIR%/xml if errorlevel 1 exit /b 1 echo. echo.Build finished. The XML files are in %BUILDDIR%/xml. goto end ) if "%1" == "pseudoxml" ( %SPHINXBUILD% -b pseudoxml %ALLSPHINXOPTS% %BUILDDIR%/pseudoxml if errorlevel 1 exit /b 1 echo. echo.Build finished. The pseudo-XML files are in %BUILDDIR%/pseudoxml. goto end ) if "%1" == "dummy" ( %SPHINXBUILD% -b dummy %ALLSPHINXOPTS% %BUILDDIR%/dummy if errorlevel 1 exit /b 1 echo. echo.Build finished. Dummy builder generates no files. goto end ) :end ================================================ FILE: docs/misc/blogs.rst ================================================ Blog posts ========== Team posts ^^^^^^^^^^ 2020 ---- * `Flexible Access Token Validation in ASP.NET Core `_ * `Resource Access in IdentityServer8 v4 and going forward `_ * `Automatic Token Management for ASP.NET Core and Worker Services 1.0 `_ * `Mutual TLS and Proof-of-Possession Tokens: Summary `_ * `Mutual TLS and Proof-of-Possession Access Tokens – Part 1: Setup `_ * `Hardening OpenID Connect/OAuth Authorize Requests (and Responses) `_ * `Hardening Refresh Tokens `_ * `OAuth 2.0: The long Road to Proof-of-Possession Access Tokens `_ * `Outsourcing IdentityServer8 Token Signing to Azure Key Vault `_ * `Using ECDSA in IdentityServer8 `_ 2019 ---- * `Scope and claims design in IdentityServer `_ * `Try Device Flow with IdentityServer8 `_ * `The State of the Implicit Flow in OAuth2 `_ * `An alternative way to secure SPAs (with ASP.NET Core, OpenID Connect, OAuth 2.0 and ProxyKit) `_ * `Automatic OAuth 2.0 Token Management in ASP.NET Core `_ * `Encrypting Identity Tokens in IdentityServer8 `_ 2018 ---- * `IdentityServer8 Update `_ * `IdentityServer and Swagger `_ * `Removing Shared Secrets for OAuth Client Authentication `_ * `Creating Your Own IdentityServer8 Storage Library `_ 2017 ---- * `Platforms where you can run IdentityServer8 `_ * `Optimizing Tokens for size `_ * `Identity vs Permissions `_ * `Bootstraping OpenID Connect: Discovery `_ * `Extending IdentityServer8 with WS-Federation Support `_ * `Announcing IdentityServer8 RC1 `_ * `Getting Started with IdentityServer 4 `_ * `IdentityServer 4 SharePoint Integration using WS-Federation `_ Community posts ^^^^^^^^^^^^^^^ * `Blazor WebAssembly authentication and authorization with IdentityServer8 `_ * `Additional API Endpoints to IdentityServer 4 `_ * `Securing Hangfire Dashboard using an OpenID Connect server (IdentityServer 4) `_ * `OAuth 2.0 - OpenID Connect & IdentityServer `_ * `Running IdentityServer8 in a Docker Container `_ * `Connecting Zendesk and IdentityServer 4 SAML 2.0 Identity Provider `_ * `IdentityServer localization using ui_locales `_ * `Self-issuing an IdentityServer8 token in an IdentityServer8 service `_ * `IdentityServer8 on the ASP.NET Team Blog `_ * `Angular2 OpenID Connect Implicit Flow with IdentityServer8 `_ * `Full Server Logout with IdentityServer8 and OpenID Connect Implicit Flow `_ * `IdentityServer8, ASP.NET Identity, Web API and Angular in a single Project `_ * `Secure your .NETCore web applications using IdentityServer 4 `_ * `ASP.NET Core IdentityServer8 Resource Owner Password Flow with custom UserRepository `_ * `Secure ASP.NET Core MVC with Angular using IdentityServer8 OpenID Connect Hybrid Flow `_ * `Adding an external Microsoft login to IdentityServer8 `_ * `Implementing Two-factor authentication with IdentityServer8 and Twilio `_ * `Security Experiments with gRPC and ASP.NET Core 3.0 `_ * `ASP.NET Core OAuth Device Flow Client with IdentityServer8 `_ * `Securing a Vue.js app using OpenID Connect Code Flow with PKCE and IdentityServer8 `_ * `Using an OData Client with an ASP.NET Core API `_ * `OpenID Connect back-channel logout using Azure Redis Cache and IdentityServer8 `_ * `Single Sign Out in IdentityServer8 with Back Channel Logout `_ ================================================ FILE: docs/misc/training.rst ================================================ Training ======== Here are some online, remote and classroom training options to learn more about ASP.NET Core identity & IdentityServer8. Identity & Access Control for modern Applications (using ASP.NET Core 2 and IdentityServer8) ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ That's our own three day flagship course (including extensive hands-on labs) that we deliver as part of conferences, on-sites and remote. The agenda and dates for public training can be found `here `_, `contact `_ us for private workshops. PluralSight courses ^^^^^^^^^^^^^^^^^^^ There are some good courses on PluralSight around identity, ASP.NET Core and IdentityServer. **new** * `Securing Angular Apps with OpenID and OAuth2 `_ * `ASP.NET Core Identity Management Playbook `_ * `Getting Started with ASP.NET Core and OAuth `_ * `Securing ASP.NET Core with OAuth2 and OpenID Connect `_ * `Understanding ASP.NET Core Security (Centralized Authentication with a Token Service) `_ **older** * `Introduction to OAuth2, OpenID Connect and JSON Web Tokens (JWT) `_ * `Web API v2 Security `_ * `Using OAuth to Secure Your ASP.NET API `_ * `OAuth2 and OpenID Connect Strategies for Angular and ASP.NET `_ ================================================ FILE: docs/misc/videos.rst ================================================ Videos ====== 2020 ^^^^ * `January [NDC London] -- Implementing OpenID Connect and OAuth 2.0 – Tips from the Trenches `_ * `January [NDC London] -- OpenID Connect & OAuth 2.0 – Security Best Practices `_ 2019 ^^^^ * `October [TDC] -- Securing Web Applications and APIs with ASP.NET Core 3.0 `_ * `January [NDC] -- Securing Web Applications and APIs with ASP.NET Core 2.2 and 3.0 `_ * `January [NDC] -- Building Clients for OpenID Connect/OAuth 2-based Systems `_ 2018 ^^^^ * `26/09 [DevConf] -- Authorization for modern Applications `_ * `17/01 [NDC London] -- IdentityServer v2 on ASP.NET Core v2 - an Update `_ * `17/01 [NDC London] -- Implementing authorization for web apps and APIs (aka PolicyServer announcement) `_ * `17/01 [DotNetRocks] -- IdentityServer and PolicyServer on DotNetRocks `_ 2017 ^^^^ * `14/09 [Microsoft Learning] -- Introduction to IdentityServer for ASP.NET Core - Brock Allen `_ * `14/06 [NDC Oslo] -- Implementing Authorization for Web Applications and APIs `_ * `22/02 [NDC Mini Copenhagen] -- IdentityServer8: New & Improved for ASP.NET Core - Dominick Baier `_ * `02/02 [DotNetRocks] -- IdentityServer8 on DotNetRocks `_ * `16/01 [NDC London] -- IdentityServer8: New and Improved for ASP.NET Core `_ * `16/01 [NDC London] -- Building JavaScript and mobile/native Clients for Token-based Architectures `_ 2016 ^^^^ * `The history of .NET identity and IdentityServer Channel9 interview `_ * `Authentication & secure API access for native & mobile Applications - Dominick Baier `_ * `ASP.NET Identity 3 - Brock Allen `_ * `Introduction to IdentityServer3 - Brock Allen `_ 2015 ^^^^ * `Securing Web APIs – Patterns & Anti-Patterns - Dominick Baier `_ * `Authentication and authorization in modern JavaScript web applications – how hard can it be? - Brock Allen `_ 2014 ^^^^ * `Unifying Authentication & Delegated API Access for Mobile, Web and the Desktop with OpenID Connect and OAuth 2 - Dominick Baier `_ ================================================ FILE: docs/quickstarts/0_overview.rst ================================================ .. _refQuickstartOverview: Overview ======== The quickstarts provide step by step instructions for various common IdentityServer scenarios. They start with the absolute basics and become more complex - it is recommended you do them in order. * adding IdentityServer to an ASP.NET Core application * configuring IdentityServer * issuing tokens for various clients * securing web applications and APIs * adding support for EntityFramework based configuration * adding support for ASP.NET Identity Every quickstart has a reference solution - you can find the code in the `samples `_ folder. Preparation ^^^^^^^^^^^ The first thing you should do is install our templates:: dotnet new -i IdentityServer8.Templates They will be used as a starting point for the various tutorials. .. note:: If you are using private NuGet sources do not forget to add the --nuget-source parameter: --nuget-source https://api.nuget.org/v3/index.json OK - let's get started! .. note:: The quickstarts target the IdentityServer 4.x and ASP.NET Core 3.1.x - there are also quickstarts for `ASP.NET Core 2 `_ and `ASP.NET Core 1 `_. ================================================ FILE: docs/quickstarts/1_client_credentials.rst ================================================ .. _refClientCredentialsQuickstart: Protecting an API using Client Credentials ========================================== The following Identity Server 4 quickstart provides step by step instructions for various common IdentityServer scenarios. These start with the absolute basics and become more complex as they progress. We recommend that you follow them in sequence. To see the full list, please go to `IdentityServer8 Quickstarts Overview `_ This first quickstart is the most basic scenario for protecting APIs using IdentityServer. In this quickstart you define an API and a Client with which to access it. The client will request an access token from the Identity Server using its client ID and secret and then use the token to gain access to the API. Source Code ^^^^^^^^^^^ As with all of these quickstarts you can find the source code for it in the `IdentityServer8 `_ repository. The project for this quickstart is `Quickstart #1: Securing an API using Client Credentials `_ Preparation ^^^^^^^^^^^ The IdentityServer templates for the dotnet CLI are a good starting point for the quickstarts. To install the templates open a console window and type the following command:: dotnet new -i IdentityServer8.Templates They will be used as a starting point for the various tutorials. Setting up the ASP.NET Core application ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ First create a directory for the application - then use our template to create an ASP.NET Core application that includes a basic IdentityServer setup, e.g.:: md quickstart cd quickstart md src cd src dotnet new is4empty -n IdentityServer This will create the following files: * ``IdentityServer.csproj`` - the project file and a ``Properties\launchSettings.json`` file * ``Program.cs`` and ``Startup.cs`` - the main application entry point * ``Config.cs`` - IdentityServer resources and clients configuration file You can now use your favorite text editor to edit or view the files. If you want to have Visual Studio support, you can add a solution file like this:: cd .. dotnet new sln -n Quickstart and let it add your IdentityServer project (keep this command in mind as we will create other projects below):: dotnet sln add .\src\IdentityServer\IdentityServer.csproj .. note:: The protocol used in this Template is ``https`` and the port is set to 5001 when running on Kestrel or a random one on IISExpress. You can change that in the ``Properties\launchSettings.json`` file. For production scenarios you should always use ``https``. Defining an API Scope ^^^^^^^^^^^^^^^^^^^^^ An API is a resource in your system that you want to protect. Resource definitions can be loaded in many ways, the template you used to create the project above shows how to use a "code as configuration" approach. The Config.cs is already created for you. Open it, update the code to look like this:: public static class Config { public static IEnumerable ApiScopes => new List { new ApiScope("api1", "My API") }; } (see the full file `here `_). .. note:: If you will be using this in production it is important to give your API a logical name. Developers will be using this to connect to your api though your Identity server. It should describe your api in simple terms to both developers and users. Defining the client ^^^^^^^^^^^^^^^^^^^ The next step is to define a client application that we will use to access our new API. For this scenario, the client will not have an interactive user, and will authenticate using the so called client secret with IdentityServer. For this, add a client definition:: public static IEnumerable Clients => new List { new Client { ClientId = "client", // no interactive user, use the clientid/secret for authentication AllowedGrantTypes = GrantTypes.ClientCredentials, // secret for authentication ClientSecrets = { new Secret("secret".Sha256()) }, // scopes that client has access to AllowedScopes = { "api1" } } }; You can think of the ClientId and the ClientSecret as the login and password for your application itself. It identifies your application to the identity server so that it knows which application is trying to connect to it. Configuring IdentityServer ^^^^^^^^^^^^^^^^^^^^^^^^^^ Loading the resource and client definitions happens in `Startup.cs `_ - update the code to look like this:: public void ConfigureServices(IServiceCollection services) { var builder = services.AddIdentityServer() .AddDeveloperSigningCredential() //This is for dev only scenarios when you don’t have a certificate to use. .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients); // omitted for brevity } That's it - your identity server should now be configured. If you run the server and navigate the browser to ``https://localhost:5001/.well-known/openid-configuration``, you should see the so-called discovery document. The discovery document is a standard endpoint in identity servers. The discovery document will be used by your clients and APIs to download the necessary configuration data. .. image:: images/1_discovery.png At first startup, IdentityServer will create a developer signing key for you, it's a file called ``tempkey.jwk``. You don't have to check that file into your source control, it will be re-created if it is not present. Adding an API ^^^^^^^^^^^^^ Next, add an API to your solution. You can either use the ASP.NET Core Web API template from Visual Studio or use the .NET CLI to create the API project as we do here. Run from within the ``src`` folder the following command:: dotnet new webapi -n Api Then add it to the solution by running the following commands:: cd .. dotnet sln add .\src\Api\Api.csproj Configure the API application to run on ``https://localhost:6001`` only. You can do this by editing the `launchSettings.json `_ file inside the Properties folder. Change the application URL setting to be:: "applicationUrl": "https://localhost:6001" The controller -------------- Add a new class called ``IdentityController``:: [Route("identity")] [Authorize] public class IdentityController : ControllerBase { [HttpGet] public IActionResult Get() { return new JsonResult(from c in User.Claims select new { c.Type, c.Value }); } } This controller will be used later to test the authorization requirement, as well as visualize the claims identity through the eyes of the API. Adding a Nuget Dependency ------------------------- In order for the configuration step to work the nuget package dependency has to be added, run this command in the root directory:: dotnet add .\\src\\api\\Api.csproj package Microsoft.AspNetCore.Authentication.JwtBearer Configuration ------------- The last step is to add the authentication services to DI (dependency injection) and the authentication middleware to the pipeline. These will: * validate the incoming token to make sure it is coming from a trusted issuer * validate that the token is valid to be used with this api (aka audience) Update `Startup` to look like this:: public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllers(); services.AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "https://localhost:5001"; options.TokenValidationParameters = new TokenValidationParameters { ValidateAudience = false }; }); } public void Configure(IApplicationBuilder app) { app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapControllers(); }); } } * ``AddAuthentication`` adds the authentication services to DI and configures ``Bearer`` as the default scheme. * ``UseAuthentication`` adds the authentication middleware to the pipeline so authentication will be performed automatically on every call into the host. * ``UseAuthorization`` adds the authorization middleware to make sure, our API endpoint cannot be accessed by anonymous clients. Navigating to the controller ``https://localhost:6001/identity`` on a browser should return a 401 status code. This means your API requires a credential and is now protected by IdentityServer. .. note:: If you are wondering, why the above code disables audience validation, have a look :ref:`here ` for a more in-depth discussion. Creating the client ^^^^^^^^^^^^^^^^^^^ The last step is to write a client that requests an access token, and then uses this token to access the API. For that, add a console project to your solution, remember to create it in the ``src``:: dotnet new console -n Client Then as before, add it to your solution using:: cd .. dotnet sln add .\src\Client\Client.csproj The token endpoint at IdentityServer implements the OAuth 2.0 protocol, and you could use raw HTTP to access it. However, we have a client library called IdentityModel, that encapsulates the protocol interaction in an easy to use API. Add the ``IdentityModel`` NuGet package to your client. This can be done either via Visual Studio's Nuget Package manager or dotnet CLI:: cd src cd client dotnet add package IdentityModel IdentityModel includes a client library to use with the discovery endpoint. This way you only need to know the base-address of IdentityServer - the actual endpoint addresses can be read from the metadata:: // discover endpoints from metadata var client = new HttpClient(); var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001"); if (disco.IsError) { Console.WriteLine(disco.Error); return; } .. note:: If you get an error connecting it may be that you are running `https` and the development certificate for ``localhost`` is not trusted. You can run ``dotnet dev-certs https --trust`` in order to trust the development certificate. This only needs to be done once. Next you can use the information from the discovery document to request a token to IdentityServer to access ``api1``:: // request token var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = disco.TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" }); if (tokenResponse.IsError) { Console.WriteLine(tokenResponse.Error); return; } Console.WriteLine(tokenResponse.Json); (full file can be found `here `_) .. note:: Copy and paste the access token from the console to `jwt.ms `_ to inspect the raw token. Calling the API ^^^^^^^^^^^^^^^ To send the access token to the API you typically use the HTTP Authorization header. This is done using the ``SetBearerToken`` extension method:: // call api var apiClient = new HttpClient(); apiClient.SetBearerToken(tokenResponse.AccessToken); var response = await apiClient.GetAsync("https://localhost:6001/identity"); if (!response.IsSuccessStatusCode) { Console.WriteLine(response.StatusCode); } else { var content = await response.Content.ReadAsStringAsync(); Console.WriteLine(JArray.Parse(content)); } (If you are in Visual Studio you can right-click on the solution and select "Multiple Startup Projects", and ensure the Api and IdentityServer will start; then run the solution; then, to step through the Client code, you can right-click on the "Client" project and select Debug... Start New Instance). The output should look like this: .. image:: images/1_client_screenshot.png .. note:: By default an access token will contain claims about the scope, lifetime (nbf and exp), the client ID (client_id) and the issuer name (iss). Authorization at the API ^^^^^^^^^^^^^^^^^^^^^^^^ Right now, the API accepts any access token issued by your identity server. In the following we will add code that allows checking for the presence of the scope in the access token that the client asked for (and got granted). For this we will use the ASP.NET Core authorization policy system. Add the following to the ``ConfigureServices`` method in ``Startup``:: services.AddAuthorization(options => { options.AddPolicy("ApiScope", policy => { policy.RequireAuthenticatedUser(); policy.RequireClaim("scope", "api1"); }); }); You can now enforce this policy at various levels, e.g. * globally * for all API endpoints * for specific controllers/actions Typically you setup the policy for all API endpoints in the routing system:: app.UseEndpoints(endpoints => { endpoints.MapControllers() .RequireAuthorization("ApiScope"); }); Further experiments ^^^^^^^^^^^^^^^^^^^ This walkthrough focused on the success path so far * client was able to request token * client could use the token to access the API You can now try to provoke errors to learn how the system behaves, e.g. * try to connect to IdentityServer when it is not running (unavailable) * try to use an invalid client id or secret to request the token * try to ask for an invalid scope during the token request * try to call the API when it is not running (unavailable) * don't send the token to the API * configure the API to require a different scope than the one in the token ================================================ FILE: docs/quickstarts/2_interactive_aspnetcore.rst ================================================ .. _refInteractiveQuickstart: Interactive Applications with ASP.NET Core ========================================== .. note:: For any pre-requisites (like e.g. templates) have a look at the :ref:`overview ` first. In this quickstart we want to add support for interactive user authentication via the OpenID Connect protocol to our IdentityServer we built in the previous chapter. Once that is in place, we will create an MVC application that will use IdentityServer for authentication. Adding the UI ^^^^^^^^^^^^^ All the protocol support needed for OpenID Connect is already built into IdentityServer. You need to provide the necessary UI parts for login, logout, consent and error. While the look & feel as well as the exact workflows will probably always differ in every IdentityServer implementation, we provide an MVC-based sample UI that you can use as a starting point. This UI can be found in the `Quickstart UI repo `_. You can clone or download this repo and drop the controllers, views, models and CSS into your IdentityServer web application. Alternatively you can use the .NET CLI (run from within the ``src/IdentityServer`` folder):: dotnet new is4ui Once you have added the MVC UI, you will also need to enable MVC, both in the DI system and in the pipeline. When you look at ``Startup.cs`` you will find comments in the ``ConfigureServices`` and ``Configure`` method that tell you how to enable MVC. .. note:: There is also a template called ``is4inmem`` which combines a basic IdentityServer including the standard UI. Run the IdentityServer application, you should now see a home page. Spend some time inspecting the controllers and models - especially the ``AccountController`` which is the main UI entry point. The better you understand them, the easier it will be to make future modifications. Most of the code lives in the "Quickstart" folder using a "feature folder" style. If this style doesn't suit you, feel free to organize the code in any way you want. Creating an MVC client ^^^^^^^^^^^^^^^^^^^^^^ Next you will create an MVC application. Use the ASP.NET Core "Web Application" (i.e. MVC) template for that. run from the src folder:: dotnet new mvc -n MvcClient cd .. dotnet sln add .\src\MvcClient\MvcClient.csproj .. note:: We recommend using the self-host option over IIS Express. The rest of the docs assume you are using self-hosting on port 5002. To add support for OpenID Connect authentication to the MVC application, you first need to add the nuget package containing the OpenID Connect handler to your project, e.g.:: dotnet add package Microsoft.AspNetCore.Authentication.OpenIdConnect ..then add the following to ``ConfigureServices`` in ``Startup``:: using System.IdentityModel.Tokens.Jwt; // ... JwtSecurityTokenHandler.DefaultMapInboundClaims = false; services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://localhost:5001"; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.SaveTokens = true; }); ``AddAuthentication`` adds the authentication services to DI. We are using a cookie to locally sign-in the user (via ``"Cookies"`` as the ``DefaultScheme``), and we set the ``DefaultChallengeScheme`` to ``oidc`` because when we need the user to login, we will be using the OpenID Connect protocol. We then use ``AddCookie`` to add the handler that can process cookies. Finally, ``AddOpenIdConnect`` is used to configure the handler that performs the OpenID Connect protocol. The ``Authority`` indicates where the trusted token service is located. We then identify this client via the ``ClientId`` and the ``ClientSecret``. ``SaveTokens`` is used to persist the tokens from IdentityServer in the cookie (as they will be needed later). .. note:: We use the so called ``authorization code`` flow with PKCE to connect to the OpenID Connect provider. See :ref:`here ` for more information on protocol flows. And then to ensure the execution of the authentication services on each request, add ``UseAuthentication`` to ``Configure`` in ``Startup``:: app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute() .RequireAuthorization(); }); .. note:: The ``RequireAuthorization`` method disables anonymous access for the entire application. You can also use the ``[Authorize]`` attribute, if you want to specify authorization on a per controller or action method basis. Also modify the home view to display the claims of the user as well as the cookie properties:: @using Microsoft.AspNetCore.Authentication

Claims

@foreach (var claim in User.Claims) {
@claim.Type
@claim.Value
}

Properties

@foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items) {
@prop.Key
@prop.Value
}
If you now navigate to the application using the browser, a redirect attempt will be made to IdentityServer - this will result in an error because the MVC client is not registered yet. Adding support for OpenID Connect Identity Scopes ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Similar to OAuth 2.0, OpenID Connect also uses the scopes concept. Again, scopes represent something you want to protect and that clients want to access. In contrast to OAuth, scopes in OIDC don't represent APIs, but identity data like user id, name or email address. Add support for the standard ``openid`` (subject id) and ``profile`` (first name, last name etc..) scopes by amending the ``IdentityResources`` property in ``Config.cs``:: public static IEnumerable IdentityResources => new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; Register the identity resources with IdentityServer in ``startup.cs``:: var builder = services.AddIdentityServer() .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients); .. note:: All standard scopes and their corresponding claims can be found in the OpenID Connect `specification `_ Adding Test Users ^^^^^^^^^^^^^^^^^ The sample UI also comes with an in-memory "user database". You can enable this in IdentityServer by adding the ``AddTestUsers`` extension method:: var builder = services.AddIdentityServer() .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users); When you navigate to the ``TestUsers`` class, you can see that two users called ``alice`` and ``bob`` as well as some identity claims are defined. You can use those users to login. Adding the MVC Client to the IdentityServer Configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ The last step is to add a new configuration entry for the MVC client to the IdentityServer. OpenID Connect-based clients are very similar to the OAuth 2.0 clients we added so far. But since the flows in OIDC are always interactive, we need to add some redirect URLs to our configuration. The client list should look like this:: public static IEnumerable Clients => new List { // machine to machine client (from quickstart 1) new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, // scopes that client has access to AllowedScopes = { "api1" } }, // interactive ASP.NET Core MVC client new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // where to redirect to after login RedirectUris = { "https://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile } } }; Testing the client ^^^^^^^^^^^^^^^^^^ Now finally everything should be in place for the new MVC client. Trigger the authentication handshake by navigating to the protected controller action. You should see a redirect to the login page of the IdentityServer. .. image:: images/3_login.png After that, the IdentityServer will redirect back to the MVC client, where the OpenID Connect authentication handler processes the response and signs-in the user locally by setting a cookie. Finally the MVC view will show the contents of the cookie. .. image:: images/3_claims.png As you can see, the cookie has two parts, the claims of the user, and some metadata. This metadata also contains the original token that was issued by the IdentityServer. Feel free to copy this token to `jwt.ms `_ to inspect its content. Adding sign-out ^^^^^^^^^^^^^^^ The very last step is to add sign-out to the MVC client. With an authentication service like IdentityServer, it is not enough to clear the local application cookies. In addition you also need to make a roundtrip to the IdentityServer to clear the central single sign-on session. The exact protocol steps are implemented inside the OpenID Connect handler, simply add the following code to some controller to trigger the sign-out:: public IActionResult Logout() { return SignOut("Cookies", "oidc"); } This will clear the local cookie and then redirect to the IdentityServer. The IdentityServer will clear its cookies and then give the user a link to return back to the MVC application. Getting claims from the UserInfo endpoint ^^^^^^^^^^^^^^^ You might have noticed that even though we've configured the client to be allowed to retrieve the ``profile`` identity scope, the claims associated with that scope (such as ``name``, ``family_name``, ``website`` etc.) don't appear in the returned token. We need to tell the client to pull remaining claims from the `UserInfo `_ endpoint by specifying scopes that the client application needs to access and setting the ``GetClaimsFromUserInfoEndpoint`` option. In the following example we're requesting the ``profile`` scope, but it could be any scope (or scopes) that the client is authorized to access:: .AddOpenIdConnect("oidc", options => { // ... options.Scope.Add("profile"); options.GetClaimsFromUserInfoEndpoint = true; // ... }); After restarting the client app, logging out, and logging back in you should see additional user claims associated with the ``profile`` identity scope displayed on the page. .. image:: images/3_additional_claims.png Further Experiments ^^^^^^^^^^^^^^^^^^^ Feel free to add more claims to the test users - and also more identity resources. The process for defining an identity resource is as follows: * add a new identity resource to the list - give it a name and specify which claims should be returned when this resource is requested * give the client access to the resource via the ``AllowedScopes`` property on the client configuration * request the resource by adding it to the ``Scopes`` collection on the OpenID Connect handler configuration in the client * (optional) if the identity resource is associated with a non-standard claim (e.g. ``myclaim1``), on the client side add the `ClaimAction `_ mapping between the claim appearing in JSON (returned from the UserInfo endpoint) and the User `Claim `_ :: using Microsoft.AspNetCore.Authentication // ... .AddOpenIdConnect("oidc", options => { // ... options.ClaimActions.MapUniqueJsonKey("myclaim1", "myclaim1"); // ... }); It is also noteworthy, that the retrieval of claims for tokens is an extensibility point - ``IProfileService``. Since we are using ``AddTestUsers``, the ``TestUserProfileService`` is used by default. You can inspect the source code `here `_ to see how it works. .. _refExternalAuthenticationQuickstart: Adding Support for External Authentication ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Next we will add support for external authentication. This is really easy, because all you really need is an ASP.NET Core compatible authentication handler. ASP.NET Core itself ships with support for Google, Facebook, Twitter, Microsoft Account and OpenID Connect. In addition you can find implementations for many other authentication providers `here `_. Adding Google support ^^^^^^^^^^^^^^^^^^^^^ To be able to use Google for authentication, you first need to register with them. This is done at their developer `console `_. Create a new project, enable the Google+ API and configure the callback address of your local IdentityServer by adding the */signin-google* path to your base-address (e.g. https://localhost:5001/signin-google). The developer console will show you a client ID and secret issued by Google - you will need that in the next step. Add the Google authentication handler to the DI of the IdentityServer host. This is done by first adding the ``Microsoft.AspNetCore.Authentication.Google`` nuget package and then adding this snippet to ``ConfigureServices`` in ``Startup``:: services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = ""; options.ClientSecret = ""; }); By default, IdentityServer configures a cookie handler specifically for the results of external authentication (with the scheme based on the constant ``IdentityServerConstants.ExternalCookieAuthenticationScheme``). The configuration for the Google handler is then using that cookie handler. Now run the MVC client and try to authenticate - you will see a Google button on the login page: .. image:: images/4_login_page.png After authentication with the MVC client, you can see that the claims are now being sourced from Google data. .. note:: If you are interested in the magic that automatically renders the Google button on the login page, inspect the ``BuildLoginViewModel`` method on the ``AccountController``. Further experiments ^^^^^^^^^^^^^^^^^^^ You can add an additional external provider. We have a `cloud-hosted demo `_ version of IdentityServer8 which you can integrate using OpenID Connect. Add the OpenId Connect handler to DI:: services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = ""; options.ClientSecret = ""; }) .AddOpenIdConnect("oidc", "Demo IdentityServer", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.SaveTokens = true; options.Authority = "https://demo.identityserver8.io/"; options.ClientId = "interactive.confidential"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; }); And now a user should be able to use the cloud-hosted demo identity provider. .. note:: The quickstart UI auto-provisions external users. As an external user logs in for the first time, a new local user is created, and all the external claims are copied over and associated with the new user. The way you deal with such a situation is completely up to you though. Maybe you want to show some sort of registration UI first. The source code for the default quickstart can be found `here `_. The controller where auto-provisioning is executed can be found `here `_. ================================================ FILE: docs/quickstarts/3_aspnetcore_and_apis.rst ================================================ .. _refAspNetCoreAndApis: ASP.NET Core and API access =========================== In the previous quickstarts we explored both API access and user authentication. Now we want to bring the two parts together. The beauty of the OpenID Connect & OAuth 2.0 combination is, that you can achieve both with a single protocol and a single exchange with the token service. So far we only asked for identity resources during the token request, once we start also including API resources, IdentityServer will return two tokens: the identity token containing the information about the authentication and session, and the access token to access APIs on behalf of the logged on user. Modifying the client configuration ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Updating the client configuration in IdentityServer is straightforward - we simply need to add the ``api1`` resource to the allowed scopes list. In addition we enable support for refresh tokens via the ``AllowOfflineAccess`` property:: new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // where to redirect to after login RedirectUris = { "https://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } } Modifying the MVC client ^^^^^^^^^^^^^^^^^^^^^^^^ All that's left to do now in the client is to ask for the additional resources via the scope parameter. This is done in the OpenID Connect handler configuration:: services.AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://localhost:5001"; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.SaveTokens = true; options.Scope.Add("api1"); options.Scope.Add("offline_access"); }); Since ``SaveTokens`` is enabled, ASP.NET Core will automatically store the resulting access and refresh token in the authentication session. You should be able to inspect the data on the page that prints out the contents of the session that you created earlier. Using the access token ^^^^^^^^^^^^^^^^^^^^^^ You can access the tokens in the session using the standard ASP.NET Core extension methods that you can find in the ``Microsoft.AspNetCore.Authentication`` namespace:: var accessToken = await HttpContext.GetTokenAsync("access_token"); For accessing the API using the access token, all you need to do is retrieve the token, and set it on your HttpClient:: public async Task CallApi() { var accessToken = await HttpContext.GetTokenAsync("access_token"); var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var content = await client.GetStringAsync("https://localhost:6001/identity"); ViewBag.Json = JArray.Parse(content).ToString(); return View("json"); } Create a view called json.cshtml that outputs the json like this::
@ViewBag.Json
Make sure the API is running, start the MVC client and call ``/home/CallApi`` after authentication. Managing the access token ^^^^^^^^^^^^^^^^^^^^^^^^^ By far the most complex task for a typical client is to manage the access token. You typically want to * request the access and refresh token at login time * cache those tokens * use the access token to call APIs until it expires * use the refresh token to get a new access token * start over ASP.NET Core has many built-in facility that can help you with those tasks (like caching or sessions), but there is still quite some work left to do. Feel free to have a look at `this `_ library, which can automate many of the boilerplate tasks. ================================================ FILE: docs/quickstarts/4_javascript_client.rst ================================================ .. _refJavaScriptQuickstart: Adding a JavaScript client ========================== .. note:: For any pre-requisites (like e.g. templates) have a look at the :ref:`overview ` first. This quickstart will show how to build a browser-based JavaScript client application (sometimes referred to as a "Single Page Application" or "`SPA`"). The user will login to IdentityServer, invoke the web API with an access token issued by IdentityServer, and logout of IdentityServer. All of this will be driven from the JavaScript running in the browser. New Project for the JavaScript client ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Create a new project for the JavaScript application. It can simply be an empty web project, an empty ASP.NET Core application, or something else like a Node.js application. This quickstart will use an ASP.NET Core application. Create a new "Empty" ASP.NET Core web application in the `~/src` directory. You can use Visual Studio or do this from the command line:: md JavaScriptClient cd JavaScriptClient dotnet new web As we have done before, with other client projects, add this project also to your solution. Run this from the root folder which has the sln file:: dotnet sln add .\src\JavaScriptClient\JavaScriptClient.csproj Modify hosting ^^^^^^^^^^^^^^^ Modify the `JavaScriptClient` project to run on https://localhost:5003. Add the static file middleware ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Given that this project is designed to run client-side, all we need ASP.NET Core to do is to serve up the static HTML and JavaScript files that will make up our application. The static file middleware is designed to do this. Register the static file middleware in `Startup.cs` in the ``Configure`` method (and at the same time remove everything else):: public void Configure(IApplicationBuilder app) { app.UseDefaultFiles(); app.UseStaticFiles(); } This middleware will now serve up static files from the application's `~/wwwroot` folder. This is where we will put our HTML and JavaScript files. If that folder does not exist in your project, create it now. Reference oidc-client ^^^^^^^^^^^^^^^^^^^^^ In one of the previous quickstarts in the ASP.NET Core MVC-based client project we used a library to handle the OpenID Connect protocol. In this quickstart in the `JavaScriptClient` project we need a similar library, except one that works in JavaScript and is designed to run in the browser. The `oidc-client library `_ is one such library. It is available via `NPM `_, `Bower `_, as well as a `direct download `_ from github. **NPM** If you want to use NPM to download `oidc-client`, then run these commands from your `JavaScriptClient` project directory:: npm i oidc-client copy node_modules\oidc-client\dist\* wwwroot This downloads the latest `oidc-client` package locally, and then copies the relevant JavaScript files into `~/wwwroot` so they can be served up by your application. **Manual download** If you want to simply download the `oidc-client` JavaScript files manually, browse to `the GitHub repository `_ and download the JavaScript files. Once downloaded, copy them into `~/wwwroot` so they can be served up by your application. Add your HTML and JavaScript files ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ Next is to add your HTML and JavaScript files to `~/wwwroot`. We will have two HTML files and one application-specific JavaScript file (in addition to the `oidc-client.js` library). In `~/wwwroot`, add a HTML file named `index.html` and `callback.html`, and add a JavaScript file called `app.js`. **index.html** This will be the main page in our application. It will simply contain the HTML for the buttons for the user to login, logout, and call the web API. It will also contain the `` **app.js** This will contain the main code for our application. The first thing is to add a helper function to log messages to the ``
``::

    function log() {
        document.getElementById('results').innerText = '';

        Array.prototype.forEach.call(arguments, function (msg) {
            if (msg instanceof Error) {
                msg = "Error: " + msg.message;
            }
            else if (typeof msg !== 'string') {
                msg = JSON.stringify(msg, null, 2);
            }
            document.getElementById('results').innerHTML += msg + '\r\n';
        });
    }

Next, add code to register ``click`` event handlers to the three buttons::

    document.getElementById("login").addEventListener("click", login, false);
    document.getElementById("api").addEventListener("click", api, false);
    document.getElementById("logout").addEventListener("click", logout, false);

Next, we can use the ``UserManager`` class from the `oidc-client` library to manage the OpenID Connect protocol. 
It requires similar configuration that was necessary in the MVC Client (albeit with different values). 
Add this code to configure and instantiate the ``UserManager``::

    var config = {
        authority: "https://localhost:5001",
        client_id: "js",
        redirect_uri: "https://localhost:5003/callback.html",
        response_type: "code",
        scope:"openid profile api1",
        post_logout_redirect_uri : "https://localhost:5003/index.html",
    };
    var mgr = new Oidc.UserManager(config);

Next, the ``UserManager`` provides a ``getUser`` API to know if the user is logged into the JavaScript application.
It uses a JavaScript ``Promise`` to return the results asynchronously. 
The returned ``User`` object has a ``profile`` property which contains the claims for the user.
Add this code to detect if the user is logged into the JavaScript application::

    mgr.getUser().then(function (user) {
        if (user) {
            log("User logged in", user.profile);
        }
        else {
            log("User not logged in");
        }
    });

Next, we want to implement the ``login``, ``api``, and ``logout`` functions. 
The ``UserManager`` provides a ``signinRedirect`` to log the user in, and a ``signoutRedirect`` to log the user out.
The ``User`` object that we obtained in the above code also has an ``access_token`` property which can be used to authenticate to a web API.
The ``access_token`` will be passed to the web API via the `Authorization` header with the `Bearer` scheme.
Add this code to implement those three functions in our application::

    function login() {
        mgr.signinRedirect();
    }

    function api() {
        mgr.getUser().then(function (user) {
            var url = "https://localhost:6001/identity";

            var xhr = new XMLHttpRequest();
            xhr.open("GET", url);
            xhr.onload = function () {
                log(xhr.status, JSON.parse(xhr.responseText));
            }
            xhr.setRequestHeader("Authorization", "Bearer " + user.access_token);
            xhr.send();
        });
    }

    function logout() {
        mgr.signoutRedirect();
    }

.. Note:: See the :ref:`client credentials quickstart ` for information on how to create the api used in the code above.

**callback.html**

This HTML file is the designated ``redirect_uri`` page once the user has logged into IdentityServer.
It will complete the OpenID Connect protocol sign-in handshake with IdentityServer. 
The code for this is all provided by the ``UserManager`` class we used earlier. 
Once the sign-in is complete, we can then redirect the user back to the main `index.html` page. 
Add this code to complete the signin process::

    
    
    
        
        
    
    
        
        
    
    

Add a client registration to IdentityServer for the JavaScript client
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Now that the client application is ready to go, we need to define a configuration entry in IdentityServer for this new JavaScript client.
In the IdentityServer project locate the client configuration (in `Config.cs`).
Add a new `Client` to the list for our new JavaScript application.
It should have the configuration listed below::

    // JavaScript Client
    new Client
    {
        ClientId = "js",
        ClientName = "JavaScript Client",
        AllowedGrantTypes = GrantTypes.Code,
        RequireClientSecret = false,
        
        RedirectUris =           { "https://localhost:5003/callback.html" },
        PostLogoutRedirectUris = { "https://localhost:5003/index.html" },
        AllowedCorsOrigins =     { "https://localhost:5003" },

        AllowedScopes = 
        {
            IdentityServerConstants.StandardScopes.OpenId,
            IdentityServerConstants.StandardScopes.Profile,
            "api1"
        }
    }

Allowing Ajax calls to the Web API with CORS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

One last bit of configuration that is necessary is to configure CORS in the web API project. 
This will allow Ajax calls to be made from `https://localhost:5003` to `https://localhost:6001`.

**Configure CORS**

Add the CORS services to the dependency injection system in ``ConfigureServices`` in `Startup.cs`::

    public void ConfigureServices(IServiceCollection services)
    {
        // ...

        services.AddCors(options =>
        {
            // this defines a CORS policy called "default"
            options.AddPolicy("default", policy =>
            {
                policy.WithOrigins("https://localhost:5003")
                    .AllowAnyHeader()
                    .AllowAnyMethod();
            });
        });
    }

Add the CORS middleware to the pipeline in ``Configure`` (just after routing)::

    public void Configure(IApplicationBuilder app)
    {
        app.UseRouting();

        app.UseCors("default");

        // ...
    }

Run the JavaScript application
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

Now you should be able to run the JavaScript client application:

.. image:: images/jsclient_not_logged_in.png

Click the "Login" button to sign the user in.
Once the user is returned back to the JavaScript application, you should see their profile information:
 
.. image:: images/jsclient_logged_in.png

And click the "API" button to invoke the web API:

.. image:: images/jsclient_api_results.png

And finally click "Logout" to sign the user out.

.. image:: images/jsclient_signed_out.png

You now have the start of a JavaScript client application that uses IdentityServer for sign-in, sign-out, and authenticating calls to web APIs.


================================================
FILE: docs/quickstarts/5_entityframework.rst
================================================
.. _refEntityFrameworkQuickstart:
Using EntityFramework Core for configuration and operational data
=================================================================

In the previous quickstarts, we created our client and scope data in code.
On startup, IdentityServer loaded this configuration data into memory.
If we wanted to modify this configuration data, we had to stop and start IdentityServer.

IdentityServer also generates temporary data, such as authorization codes, consent choices, and refresh tokens.
By default, these are also stored in-memory.

To move this data into a database that is persistent between restarts and across multiple IdentityServer instances, we can use the IdentityServer8 Entity Framework library.

.. Note:: In addition to manually configuring EF support, there is also an IdentityServer template to create a new project with EF support, using ``dotnet new is4ef``.

IdentityServer8.EntityFramework
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
``IdentityServer8.EntityFramework`` implements the required stores and services using the following DbContexts:

    * ConfigurationDbContext - used for configuration data such as clients, resources, and scopes
    * PersistedGrantDbContext - used for temporary operational data such as authorization codes, and refresh tokens

These contexts are suitable for any Entity Framework Core compatible relational database.

You can find these contexts, their entities, and the IdentityServer8 stores that use them in the ``IdentityServer8.EntityFramework.Storage`` nuget package.

You can find the extension methods to register them in your IdentityServer in ``IdentityServer8.EntityFramework``, which we will do now::

    dotnet add package IdentityServer8.EntityFramework

Using SqlServer
^^^^^^^^^^^^^^^

For this quickstart, we will use the LocalDb version of SQLServer that comes with Visual Studio.
To add SQL Server support to our IdentityServer project, you’ll need the following nuget package::

    dotnet add package Microsoft.EntityFrameworkCore.SqlServer

Database Schema Changes and Using EF Migrations
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``IdentityServer8.EntityFramework.Storage`` package contains entity classes that map from IdentityServer’s models.
As IdentityServer’s models change, so will the entity classes in ``IdentityServer8.EntityFramework.Storage``.
As you use ``IdentityServer8.EntityFramework.Storage`` and upgrade over time, you are responsible for your database schema and changes necessary to that schema as the entity classes change.
One approach for managing those changes is to use `EF migrations `_, which is what we’ll use in this quickstart.
If migrations are not your preference, then you can manage the schema changes in any way you see fit.

.. Note:: You can find the `latest SQL scripts `_ for SqlServer in the IdentityServer8.EntityFramework.Storage repository.

Configuring the Stores
^^^^^^^^^^^^^^^^^^^^^^

To start using these stores, you’ll need to replace any existing calls to ``AddInMemoryClients``, ``AddInMemoryIdentityResources``, ``AddInMemoryApiScopes``, ``AddInMemoryApiResources``, and ``AddInMemoryPersistedGrants`` in your ``ConfigureServices`` method in `Startup.cs` with ``AddConfigurationStore`` and ``AddOperationalStore``.

These methods each require a ``DbContextOptionsBuilder``, meaning your code will look something like this::

    var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
    const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer8.Quickstart.EntityFramework-4.0.0;trusted_connection=yes;";

    services.AddIdentityServer()
        .AddTestUsers(TestUsers.Users)
        .AddConfigurationStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString,
                sql => sql.MigrationsAssembly(migrationsAssembly));
        })
        .AddOperationalStore(options =>
        {
            options.ConfigureDbContext = b => b.UseSqlServer(connectionString,
                sql => sql.MigrationsAssembly(migrationsAssembly));
        });

You might need these namespaces added to the file::

    using Microsoft.EntityFrameworkCore;
    using System.Reflection;


Because we are using EF migrations in this quickstart, the call to ``MigrationsAssembly`` is used to inform Entity Framework that the host project will contain the migrations code.
This is necessary since the host project is in a different assembly than the one that contains the ``DbContext`` classes.

Adding Migrations
^^^^^^^^^^^^^^^^^

Once the IdentityServer has been configured to use Entity Framework, we’ll need to generate some migrations.

To create migrations, you will need to install the Entity Framework Core CLI on your machine and the ``Microsoft.EntityFrameworkCore.Design`` nuget package in IdentityServer::

    dotnet tool install --global dotnet-ef
    dotnet add package Microsoft.EntityFrameworkCore.Design

To create the migrations, open a command prompt in the IdentityServer project directory and run the following two commands::

    dotnet ef migrations add InitialIdentityServerPersistedGrantDbMigration -c PersistedGrantDbContext -o Data/Migrations/IdentityServer/PersistedGrantDb
    dotnet ef migrations add InitialIdentityServerConfigurationDbMigration -c ConfigurationDbContext -o Data/Migrations/IdentityServer/ConfigurationDb

You should now see a ``~/Data/Migrations/IdentityServer`` folder in your project containing the code for your newly created migrations.

Initializing the Database
^^^^^^^^^^^^^^^^^^^^^^^^^

Now that we have the migrations, we can write code to create the database from the migrations.
We can also seed the database with the in-memory configuration data that we already defined in the previous quickstarts.

.. Note:: The approach used in this quickstart is used to make it easy to get IdentityServer up and running. You should devise your own database creation and maintenance strategy that is appropriate for your architecture.

In `Startup.cs` add this method to help initialize the database::

    private void InitializeDatabase(IApplicationBuilder app)
    {
        using (var serviceScope = app.ApplicationServices.GetService().CreateScope())
        {
            serviceScope.ServiceProvider.GetRequiredService().Database.Migrate();

            var context = serviceScope.ServiceProvider.GetRequiredService();
            context.Database.Migrate();
            if (!context.Clients.Any())
            {
                foreach (var client in Config.Clients)
                {
                    context.Clients.Add(client.ToEntity());
                }
                context.SaveChanges();
            }

            if (!context.IdentityResources.Any())
            {
                foreach (var resource in Config.IdentityResources)
                {
                    context.IdentityResources.Add(resource.ToEntity());
                }
                context.SaveChanges();
            }

            if (!context.ApiScopes.Any())
            {
                foreach (var resource in Config.ApiScopes)
                {
                    context.ApiScopes.Add(resource.ToEntity());
                }
                context.SaveChanges();
            }
        }
    }

The above code may require you to add the following namespaces to your file::

    using System.Linq;
    using IdentityServer8.EntityFramework.DbContexts;
    using IdentityServer8.EntityFramework.Mappers;

And then we can invoke this from the ``Configure`` method::

    public void Configure(IApplicationBuilder app)
    {
        // this will do the initial DB population
        InitializeDatabase(app);

        // the rest of the code that was already here
        // ...
    }

Now if you run the IdentityServer project, the database should be created and seeded with the quickstart configuration data.
You should be able to use SQL Server Management Studio or Visual Studio to connect and inspect the data.

.. image:: images/ef_database.png

.. Note:: The above ``InitializeDatabase`` helper API is convenient to seed the database, but this approach is not ideal to leave in to execute each time the application runs. Once your database is populated, consider removing the call to the API.

Run the client applications
^^^^^^^^^^^^^^^^^^^^^^^^^^^

You should now be able to run any of the existing client applications and sign-in, get tokens, and call the API -- all based upon the database configuration.


================================================
FILE: docs/quickstarts/6_aspnet_identity.rst
================================================
.. _refAspNetIdentityQuickstart:
Using ASP.NET Core Identity
===========================

.. note:: For any pre-requisites (like e.g. templates) have a look at the :ref:`overview ` first.

IdentityServer is designed for flexibility and part of that is allowing you to use any database you want for your users and their data (including passwords).
If you are starting with a new user database, then ASP.NET Core Identity is one option you could choose.
This quickstart shows how to use ASP.NET Core Identity with IdentityServer.

The approach this quickstart takes to using ASP.NET Core Identity is to create a new project for the IdentityServer host.
This new project will replace the prior IdentityServer project we built up in the previous quickstarts.
The reason for this new project is due to the differences in UI assets when using ASP.NET Core Identity (mainly around the differences in login and logout).
All the other projects in this solution (for the clients and the API) will remain the same.

.. Note:: This quickstart assumes you are familiar with how ASP.NET Core Identity works. If you are not, it is recommended that you first `learn about it `_.

New Project for ASP.NET Core Identity
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The first step is to add a new project for ASP.NET Core Identity to your solution.
We provide a template that contains the minimal UI assets needed to ASP.NET Core Identity with IdentityServer.
You will eventually delete the old project for IdentityServer, but there are some items that you will need to migrate over.

Start by creating a new IdentityServer project that will use ASP.NET Core Identity::
    
    cd quickstart/src
    dotnet new is4aspid -n IdentityServerAspNetIdentity

When prompted to "seed" the user database, choose "Y" for "yes".
This populates the user database with our "alice" and "bob" users. 
Their passwords are "Pass123$".

.. Note:: The template uses Sqlite as the database for the users, and EF migrations are pre-created in the template. If you wish to use a different database provider, you will need to change the provider used in the code and re-create the EF migrations.

Inspect the new project
^^^^^^^^^^^^^^^^^^^^^^^

Open the new project in the editor of your choice, and inspect the generated code.
Be sure to look at:

IdentityServerAspNetIdentity.csproj
-----------------------------------

Notice the reference to `IdentityServer8.AspNetIdentity`. 
This NuGet package contains the ASP.NET Core Identity integration components for IdentityServer.

Startup.cs
----------

In `ConfigureServices` notice the necessary ``AddDbContext`` and ``AddIdentity`` calls are done to configure ASP.NET Core Identity.

Also notice that much of the same IdentityServer configuration you did in the previous quickstarts is already done.
The template uses the in-memory style for clients and resources, and those are sourced from `Config.cs`.

Finally, notice the addition of the new call to ``AddAspNetIdentity``.
``AddAspNetIdentity`` adds the integration layer to allow IdentityServer to access the user data for the ASP.NET Core Identity user database.
This is needed when IdentityServer must add claims for the users into tokens.

Note that ``AddIdentity`` must be invoked before ``AddIdentityServer``.

Config.cs
-----------

`Config.cs` contains the hard-coded in-memory clients and resource definitions.
To keep the same clients and API working as the prior quickstarts, we need to copy over the configuration data from the old IdentityServer project into this one.
Do that now, and afterwards `Config.cs` should look like this::

    public static class Config
    {
        public static IEnumerable IdentityResources =>
            new List
            {
                new IdentityResources.OpenId(),
                new IdentityResources.Profile(),
            };

        public static IEnumerable ApiScopes =>
            new List
            {
                new ApiScope("api1", "My API")
            };

        public static IEnumerable Clients =>
            new List
            {
                // machine to machine client
                new Client
                {
                    ClientId = "client",
                    ClientSecrets = { new Secret("secret".Sha256()) },

                    AllowedGrantTypes = GrantTypes.ClientCredentials,
                    // scopes that client has access to
                    AllowedScopes = { "api1" }
                },
                
                // interactive ASP.NET Core MVC client
                new Client
                {
                    ClientId = "mvc",
                    ClientSecrets = { new Secret("secret".Sha256()) },

                    AllowedGrantTypes = GrantTypes.Code,
                    
                    // where to redirect to after login
                    RedirectUris = { "https://localhost:5002/signin-oidc" },

                    // where to redirect to after logout
                    PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" },

                    AllowedScopes = new List
                    {
                        IdentityServerConstants.StandardScopes.OpenId,
                        IdentityServerConstants.StandardScopes.Profile,
                        "api1"
                    }
                }
            };
    }


At this point, you no longer need the old IdentityServer project.

Program.cs and SeedData.cs
--------------------------

`Program.cs`'s ``Main`` is a little different than most ASP.NET Core projects.
Notice how this looks for a command line argument called `/seed` which is used as a flag to seed the users in the ASP.NET Core Identity database.

Look at the ``SeedData`` class' code to see how the database is created and the first users are created.

AccountController
-----------------

The last code to inspect in this template is the ``AccountController``. 
This contains a slightly different login and logout code than the prior quickstart and templates.
Notice the use of the ``SignInManager`` and ``UserManager`` from ASP.NET Core Identity to validate credentials and manage the authentication session.

Much of the rest of the code is the same from the prior quickstarts and templates.

Logging in with the MVC client
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

At this point, you should be able to run all of the existing clients and samples.
One exception is the `ResourceOwnerClient` -- the password will need to be updated to ``Pass123$`` from ``password``.

Launch the MVC client application, and you should be able to click the "Secure" link to get logged in.

.. image:: images/aspid_mvc_client.png

You should be redirected to the ASP.NET Core Identity login page.
Login with your newly created user:

.. image:: images/aspid_login.png

After login you see the normal consent page. 
After consent you will be redirected back to the MVC client application where your user's claims should be listed.

.. image:: images/aspid_claims.png

You should also be able to click "Call API using application identity" to invoke the API on behalf of the user:

.. image:: images/aspid_api_claims.png

And now you're using users from ASP.NET Core Identity in IdentityServer.

What's Missing?
^^^^^^^^^^^^^^^

Much of the rest of the code in this template is similar to the other quickstart and templates we provide.
The one thing you will notice that is missing from this template is UI code for user registration, password reset, and the other things you might expect from the Visual Studio ASP.NET Core Identity template.

Given the variety of requirements and different approaches to using ASP.NET Core Identity, our template deliberately does not provide those features.
You are expected to know how ASP.NET Core Identity works sufficiently well to add those features to your project.
Alternatively, you can create a new project based on the Visual Studio ASP.NET Core Identity template and add the IdentityServer features you have learned about in these quickstarts to that project.


================================================
FILE: docs/quickstarts/community.rst
================================================
Community quickstarts & samples
===============================
These samples are not maintained by the IdentityServer organization.
The IdentityServer organization happily links to community samples, but can't make any guarantees about the samples.
Please contact the authors directly.

Various ASP.NET Core security samples
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
https://github.com/leastprivilege/AspNetCoreSecuritySamples

Co-hosting IdentityServer8 and a Web API
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This sample shows how to host an API in the same host as the IdentityServer that is protecting the API.

https://github.com/brockallen/IdentityServerAndApi

IdentityServer8 samples for MongoDB
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* IdentityServer8-mongo: Similar to Quickstart EntityFramework configuration but using MongoDB for the configuration data.
* IdentityServer8-mongo-AspIdentity: More elaborated sample based on uses ASP.NET Identity for identity management that uses using MongoDB for the configuration data

https://github.com/souzartn/IdentityServer8.Samples.Mongo

Exchanging external tokens from Facebook, Google and Twitter
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Shows how to exchange an external authentication token to an identity server acesss token using an extension grant

https://github.com/waqaskhan540/IdentityServerExternalAuth


.NET Core and ASP.NET Core "Platform" scenario
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Shows an interaction of trusted "internal" applications and "external" applications with .NET Core 2.0 and ASP.NET Core 2.0 applications
https://github.com/BenjaminAbt/Samples.AspNetCore-IdentityServer8


Securing a Node API with tokens from IdentityServer8 using JWKS
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
* Shows how to secure a Node (Express) API using the JWKS endpoint and RS256 algorithm from IdentityServer8.
* Provides an alternative to the NodeJsApi sample from IdentityServer samples using higher quality - production ready modules.

https://github.com/lyphtec/idsvr4-node-jwks


================================================
FILE: docs/readme.md
================================================
# IdentityServer8 documentation

The folder contains the documentation for IdentityServer8.

We are using [Read the docs](https://readthedocs.org/) to host the documentation and the rendered version
can be found [here](https://IdentityServer8.readthedocs.io).

Doc pages are authored in ReStructuredText (RST) - you can find a primer [here](http://www.sphinx-doc.org/en/stable/rest.html).

You can find more information about RTD and Sphinx under the following links:

* [Read the Docs documentation](https://docs.readthedocs.io/en/latest/index.html)
* [Sphinx](http://www.sphinx-doc.org/)
* [Getting started Screencast](https://www.youtube.com/watch?feature=player_embedded&v=oJsUvBQyHBs)


================================================
FILE: docs/reference/api_resource.rst
================================================
.. _refApiResource:
API Resource
=================
This class models an API resource.

``Enabled``
    Indicates if this resource is enabled and can be requested. Defaults to true.
``Name``
    The unique name of the API. This value is used for authentication with introspection and will be added to the audience of the outgoing access token.
``DisplayName``
    This value can be used e.g. on the consent screen.
``Description``
    This value can be used e.g. on the consent screen.
``ApiSecrets``
    The API secret is used for the introspection endpoint. The API can authenticate with introspection using the API name and secret.
``AllowedAccessTokenSigningAlgorithms``
    List of allowed signing algorithms for access token. If empty, will use the server default signing algorithm.
``UserClaims``
    List of associated user claim types that should be included in the access token.
``Scopes``
    List of API scope names.

Defining API resources in appsettings.json
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
The ``AddInMemoryApiResource`` extensions method also supports adding API resources from the ASP.NET Core configuration file::

    "IdentityServer": {
        "IssuerUri": "urn:sso.company.com",
        "ApiResources": [
            {
                "Name": "resource1",
                "DisplayName": "Resource #1",

                "Scopes": [
                    "resource1.scope1",
                    "shared.scope"
                ]
            },
            {
                "Name": "resource2",
                "DisplayName": "Resource #2",
                
                "UserClaims": [
                    "name",
                    "email"
                ],

                "Scopes": [
                    "resource2.scope1",
                    "shared.scope"
                ]
            }
        ]
    }

Then pass the configuration section to the ``AddInMemoryApiResource`` method::

    AddInMemoryApiResources(configuration.GetSection("IdentityServer:ApiResources"))


================================================
FILE: docs/reference/api_scope.rst
================================================
.. _refApiScope:
API Scope
=================
This class models an OAuth scope.

``Enabled``
    Indicates if this resource is enabled and can be requested. Defaults to true.
``Name``
    The unique name of the API. This value is used for authentication with introspection and will be added to the audience of the outgoing access token.
``DisplayName``
    This value can be used e.g. on the consent screen.
``Description``
    This value can be used e.g. on the consent screen.
``UserClaims``
    List of associated user claim types that should be included in the access token.

Defining API scope in appsettings.json
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

The ``AddInMemoryApiResource`` extension method also supports adding clients from the ASP.NET Core configuration file::

    "IdentityServer": {
        "IssuerUri": "urn:sso.company.com",
        "ApiScopes": [
            {
                "Name": "IdentityServerApi"
            },
            {
                "Name": "resource1.scope1"
            },
            {
                "Name": "resource2.scope1"
            },
            {
                "Name": "scope3"
            },
            {
                "Name": "shared.scope"
            },
            {
                "Name": "transaction",
                "DisplayName": "Transaction",
                "Description": "A transaction"
            }
        ]
    }

Then pass the configuration section to the ``AddInMemoryApiScopes`` method::

    AddInMemoryApiScopes(configuration.GetSection("IdentityServer:ApiScopes"))


================================================
FILE: docs/reference/aspnet_identity.rst
================================================
.. _refAspNetId:
ASP.NET Identity Support
========================

An ASP.NET Identity-based implementation is provided for managing the identity database for users of IdentityServer.
This implementation implements the extensibility points in IdentityServer needed to load identity data for your users to emit claims into tokens.

The repo for this support is located `here `_ and the NuGet package is `here `_.

To use this library, configure ASP.NET Identity normally. 
Then use the ``AddAspNetIdentity`` extension method after the call to ``AddIdentityServer``::

    public void ConfigureServices(IServiceCollection services)
    {
        services.AddIdentity()
            .AddEntityFrameworkStores()
            .AddDefaultTokenProviders();

        services.AddIdentityServer()
            .AddAspNetIdentity();
    }

``AddAspNetIdentity`` requires as a generic parameter the class that models your user for ASP.NET Identity (and the same one passed to ``AddIdentity`` to configure ASP.NET Identity).
This configures IdentityServer to use the ASP.NET Identity implementations of ``IUserClaimsPrincipalFactory``, ``IResourceOwnerPasswordValidator``, and ``IProfileService``.
It also configures some of ASP.NET Identity's options for use with IdentityServer (such as claim types to use and authentication cookie settings).

When using your own implementation of ``IUserClaimsPrincipalFactory``, make sure that you register it before calling the IdentityServer ``AddAspNetIdentity`` extension method.

================================================
FILE: docs/reference/client.rst
================================================
.. _refClient:
Client
======
The ``Client`` class models an OpenID Connect or OAuth 2.0 client - 
e.g. a native application, a web application or a JS-based application.

Basics
^^^^^^
``Enabled``
    Specifies if client is enabled. Defaults to `true`.
``ClientId``
    Unique ID of the client
``ClientSecrets``
    List of client secrets - credentials to access the token endpoint.
``RequireClientSecret``
    Specifies whether this client needs a secret to request tokens from the token endpoint (defaults to ``true``)
``RequireRequestObject``
    Specifies whether this client needs to wrap the authorize request parameters in a JWT (defaults to ``false``)
``AllowedGrantTypes``
    Specifies the grant types the client is allowed to use. Use the ``GrantTypes`` class for common combinations.
``RequirePkce``
    Specifies whether clients using an authorization code based grant type must send a proof key (defaults to ``true``).
``AllowPlainTextPkce``
    Specifies whether clients using PKCE can use a plain text code challenge (not recommended - and default to ``false``)
``RedirectUris``
    Specifies the allowed URIs to return tokens or authorization codes to
``AllowedScopes``
    By default a client has no access to any resources - specify the allowed resources by adding the corresponding scopes names
``AllowOfflineAccess``
    Specifies whether this client can request refresh tokens (be requesting the ``offline_access`` scope)
``AllowAccessTokensViaBrowser``
    Specifies whether this client is allowed to receive access tokens via the browser. 
    This is useful to harden flows that allow multiple response types 
    (e.g. by disallowing a hybrid flow client that is supposed to use `code id_token` to add the `token` response type 
    and thus leaking the token to the browser.
``Properties``
    Dictionary to hold any custom client-specific values as needed.

Authentication/Logout
^^^^^^^^^^^^^^^^^^^^^
``PostLogoutRedirectUris``
    Specifies allowed URIs to redirect to after logout. See the `OIDC Connect Session Management spec `_ for more details.
``FrontChannelLogoutUri``
    Specifies logout URI at client for HTTP based front-channel logout. See the `OIDC Front-Channel spec `_ for more details.
``FrontChannelLogoutSessionRequired``
    Specifies if the user's session id should be sent to the FrontChannelLogoutUri. Defaults to true.
``BackChannelLogoutUri``
    Specifies logout URI at client for HTTP based back-channel logout. See the `OIDC Back-Channel spec `_ for more details.
``BackChannelLogoutSessionRequired``
    Specifies if the user's session id should be sent in the request to the BackChannelLogoutUri. Defaults to true.
``EnableLocalLogin``
    Specifies if this client can use local accounts, or external IdPs only. Defaults to `true`.
``IdentityProviderRestrictions``
    Specifies which external IdPs can be used with this client (if list is empty all IdPs are allowed). Defaults to empty.
``UserSsoLifetime`` `added in 2.3`
    The maximum duration (in seconds) since the last time the user authenticated. Defaults to ``null``.
    You can adjust the lifetime of a session token to control when and how often a user is required to reenter credentials instead of being silently authenticated, when using a web application.

Token
^^^^^
``IdentityTokenLifetime``
    Lifetime to identity token in seconds (defaults to 300 seconds / 5 minutes)
``AllowedIdentityTokenSigningAlgorithms``
    List of allowed signing algorithms for identity token. If empty, will use the server default signing algorithm.
``AccessTokenLifetime``
    Lifetime of access token in seconds (defaults to 3600 seconds / 1 hour)
``AuthorizationCodeLifetime``
    Lifetime of authorization code in seconds (defaults to 300 seconds / 5 minutes)
``AbsoluteRefreshTokenLifetime``
    Maximum lifetime of a refresh token in seconds. Defaults to 2592000 seconds / 30 days
``SlidingRefreshTokenLifetime``
    Sliding lifetime of a refresh token in seconds. Defaults to 1296000 seconds / 15 days
``RefreshTokenUsage``
    ``ReUse`` the refresh token handle will stay the same when refreshing tokens
    
    ``OneTime`` the refresh token handle will be updated when refreshing tokens. This is the default.
``RefreshTokenExpiration``
    ``Absolute`` the refresh token will expire on a fixed point in time (specified by the AbsoluteRefreshTokenLifetime). This is the default.
    
    ``Sliding`` when refreshing the token, the lifetime of the refresh token will be renewed (by the amount specified in SlidingRefreshTokenLifetime). The lifetime will not exceed `AbsoluteRefreshTokenLifetime`.
``UpdateAccessTokenClaimsOnRefresh``
    Gets or sets a value indicating whether the access token (and its claims) should be updated on a refresh token request.
``AccessTokenType``
    Specifies whether the access token is a reference token or a self contained JWT token (defaults to `Jwt`).
``IncludeJwtId``
    Specifies whether JWT access tokens should have an embedded unique ID (via the `jti` claim). Defaults to ``true``.
``AllowedCorsOrigins``
    If specified, will be used by the default CORS policy service implementations (In-Memory and EF) to build a CORS policy for JavaScript clients.
``Claims``
    Allows settings claims for the client (will be included in the access token).
``AlwaysSendClientClaims``
    If set, the client claims will be sent for every flow. If not, only for client credentials flow (default is `false`)
``AlwaysIncludeUserClaimsInIdToken``
    When requesting both an id token and access token, should the user claims always be added to the id token instead of requiring the client to use the userinfo endpoint. Default is `false`.
``ClientClaimsPrefix``
    If set, the prefix client claim types will be prefixed with. Defaults to `client_`. The intent is to make sure they don't accidentally collide with user claims.
``PairWiseSubjectSalt``
    Salt value used in pair-wise subjectId generation for users of this client.

Consent Screen
^^^^^^^^^^^^^^
``RequireConsent``
    Specifies whether a consent screen is required. Defaults to ``false``.
``AllowRememberConsent``
    Specifies whether user can choose to store consent decisions. Defaults to ``true``.
``ConsentLifetime``
    Lifetime of a user consent in seconds. Defaults to null (no expiration).
``ClientName``
    Client display name (used for logging and consent screen)
``ClientUri``
    URI to further information about client (used on consent screen)
``LogoUri``
    URI to client logo (used on consent screen)

Device flow
^^^^^^^^^^^
``UserCodeType``
    Specifies the type of user code to use for the client. Otherwise falls back to default.
``DeviceCodeLifetime``
    Lifetime to device code in seconds (defaults to 300 seconds / 5 minutes)


================================================
FILE: docs/reference/deviceflow_interactionservice.rst
================================================
Device Flow Interaction Service
==================================

The ``IDeviceFlowInteractionService`` interface is intended to provide services to be used by the user interface to communicate with IdentityServer during device flow authorization.
It is available from the dependency injection system and would normally be injected as a constructor parameter into your MVC controllers for the user interface of IdentityServer.

IDeviceFlowInteractionService APIs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

``GetAuthorizationContextAsync``
    Returns the ``DeviceFlowAuthorizationRequest`` based on the ``userCode`` passed to the login or consent pages.

``DeviceFlowInteractionResult``
    Completes device authorization for the given ``userCode``.

DeviceFlowAuthorizationRequest
^^^^^^^^^^^^^^^^^^^^
``ClientId``
    The client identifier that initiated the request.
``ScopesRequested``
    The scopes requested from the authorization request.

DeviceFlowInteractionResult
^^^^^^^^^^^^^^^^^^^^^^^^^^^
``IsError``
    Specifies if the authorization request errored.
``ErrorDescription``
    Error description upon failure.


================================================
FILE: docs/reference/ef.rst
================================================
.. _refEF:
Entity Framework Support
========================

An EntityFramework-based implementation is provided for the configuration and operational data extensibility points in IdentityServer.
The use of EntityFramework allows any EF-supported database to be used with this library.

The code for this library is located `here `_ (with the underlying storage code `here `_) and the NuGet package is `here `_.

The features provided by this library are broken down into two main areas: configuration store and operational store support.
These two different areas can be used independently or together, based upon the needs of the hosting application.

Configuration Store support for Clients, Resources, and CORS settings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If client, identity resource, API resource, or CORS data is desired to be loaded from a EF-supported database 
(rather than use in-memory configuration), then the configuration store can be used.
This support provides implementations of the ``IClientStore``, ``IResourceStore``, and the ``ICorsPolicyService`` extensibility points.
These implementations use a ``DbContext``-derived class called ``ConfigurationDbContext`` to model the tables in the database.

To use the configuration store support, use the ``AddConfigurationStore`` extension method after the call to ``AddIdentityServer``::

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer8.EntityFramework-2.0.0;trusted_connection=yes;";
        var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
        
        services.AddIdentityServer()
            // this adds the config data from DB (clients, resources, CORS)
            .AddConfigurationStore(options =>
            {
                options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));
            });
    }

To configure the configuration store, use the ``ConfigurationStoreOptions`` options object passed to the configuration callback.

ConfigurationStoreOptions
^^^^^^^^^^^^^^^^^^^^^^^^^
This options class contains properties to control the configuration store and ``ConfigurationDbContext``.

``ConfigureDbContext``
    Delegate of type ``Action`` used as a callback to configure the underlying ``ConfigurationDbContext``.
    The delegate can configure the ``ConfigurationDbContext`` in the same way if EF were being used directly with ``AddDbContext``, which allows any EF-supported database to be used.
``DefaultSchema``
    Allows setting the default database schema name for all the tables in the ``ConfigurationDbContext``
    ::
            options.DefaultSchema = "myConfigurationSchema";      

If you need to change the schema for the Migration History Table, you can chain another action to the ``UseSqlServer``::

    options.ConfigureDbContext = b =>
        b.UseSqlServer(connectionString,
            sql => sql.MigrationsAssembly(migrationsAssembly).MigrationsHistoryTable("MyConfigurationMigrationTable", "myConfigurationSchema"));

Operational Store support for persisted grants
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

If :ref:`persisted grants ` are desired to be loaded from a EF-supported database (rather than the default in-memory database), then the operational store can be used.
This support provides implementations of the ``IPersistedGrantStore`` extensibility point.
The implementation uses a ``DbContext``-derived class called ``PersistedGrantDbContext`` to model the table in the database.

To use the operational store support, use the ``AddOperationalStore`` extension method after the call to ``AddIdentityServer``::

    public IServiceProvider ConfigureServices(IServiceCollection services)
    {
        const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer8.EntityFramework-2.0.0;trusted_connection=yes;";
        var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
        
        services.AddIdentityServer()
            // this adds the operational data from DB (codes, tokens, consents)
            .AddOperationalStore(options =>
            {
                options.ConfigureDbContext = builder =>
                    builder.UseSqlServer(connectionString,
                        sql => sql.MigrationsAssembly(migrationsAssembly));

                // this enables automatic token cleanup. this is optional.
                options.EnableTokenCleanup = true;
                options.TokenCleanupInterval = 3600; // interval in seconds (default is 3600)
            });
    }

To configure the operational store, use the ``OperationalStoreOptions`` options object passed to the configuration callback.

OperationalStoreOptions
^^^^^^^^^^^^^^^^^^^^^^^
This options class contains properties to control the operational store and ``PersistedGrantDbContext``.

``ConfigureDbContext``
    Delegate of type ``Action`` used as a callback to configure the underlying ``PersistedGrantDbContext``.
    The delegate can configure the ``PersistedGrantDbContext`` in the same way if EF were being used directly with ``AddDbContext``, which allows any EF-supported database to be used.
``DefaultSchema``
    Allows setting the default database schema name for all the tables in the ``PersistedGrantDbContext``.
``EnableTokenCleanup``
    Indicates whether expired grants will be automatically cleaned up from the database. The default is ``false``.
``TokenCleanupInterval``
    The token cleanup interval (in seconds). The default is 3600 (1 hour).

.. note:: The token cleanup feature does *not* remove persisted grants that are *consumed* (see :ref:`persisted grants `).

Database creation and schema changes across different versions of IdentityServer
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

It is very likely that across different versions of IdentityServer (and the EF support) that the database schema will change to accommodate new and changing features.

We do not provide any support for creating your database or migrating your data from one version to another. 
You are expected to manage the database creation, schema changes, and data migration in any way your organization sees fit.

Using EF migrations is one possible approach to this. 
If you do wish to use migrations, then see the :ref:`EF quickstart ` for samples on how to get started, or consult the Microsoft `documentation on EF migrations `_.

We also publish `sample SQL scripts `_ for the current version of the database schema.


================================================
FILE: docs/reference/grant_validation_result.rst
================================================
.. _refGrantValidationResult:
GrantValidationResult
=====================

The ``GrantValidationResult`` class models the outcome of grant validation for extensions grants and resource owner password grants.

The most common usage is to either new it up using an identity (success case)::

    context.Result = new GrantValidationResult(
        subject: "818727", 
        authenticationMethod: "custom", 
        claims: optionalClaims);

...or using an error and description (failure case)::

    context.Result = new GrantValidationResult(
        TokenRequestErrors.InvalidGrant, 
        "invalid custom credential");

In both case you can pass additional custom values that will be included in the token response.

================================================
FILE: docs/reference/identity_resource.rst
================================================
.. _refIdentityResource:
Identity Resource
=================

This class models an identity resource.

``Enabled``
    Indicates if this resource is enabled and can be requested. Defaults to true.
``Name``
    The unique name of the identity resource. This is the value a client will use for the scope parameter in the authorize request.
``DisplayName``
    This value will be used e.g. on the consent screen.
``Description``
    This value will be used e.g. on the consent screen.
``Required``
    Specifies whether the user can de-select the scope on the consent screen (if the consent screen wants to implement such a feature). Defaults to false.
``Emphasize``
    Specifies whether the consent screen will emphasize this scope (if the consent screen wants to implement such a feature). Use this setting for sensitive or important scopes. Defaults to false.
``ShowInDiscoveryDocument``
    Specifies whether this scope is shown in the discovery document. Defaults to true.
``UserClaims``
    List of associated user claim types that should be included in the identity token.

================================================
FILE: docs/reference/interactionservice.rst
================================================
.. _refInteractionService:
IdentityServer Interaction Service
==================================

The ``IIdentityServerInteractionService`` interface is intended to provide services to be used by the user interface to communicate with IdentityServer, mainly pertaining to user interaction.
It is available from the dependency injection system and would normally be injected as a constructor parameter into your MVC controllers for the user interface of IdentityServer.

IIdentityServerInteractionService APIs
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

``GetAuthorizationContextAsync``
    Returns the ``AuthorizationRequest`` based on the ``returnUrl`` passed to the login or consent pages.

``IsValidReturnUrl``
    Indicates if the ``returnUrl`` is a valid URL for redirect after login or consent.

``GetErrorContextAsync``
    Returns the ``ErrorMessage`` based on the ``errorId`` passed to the error page.

``GetLogoutContextAsync``
    Returns the ``LogoutRequest`` based on the ``logoutId`` passed to the logout page.

``CreateLogoutContextAsync``
    Used to create a ``logoutId`` if there is not one presently.
    This creates a cookie capturing all the current state needed for signout and the ``logoutId`` identifies that cookie.
    This is typically used when there is no current ``logoutId`` and the logout page must capture the current user's state needed for sign-out prior to redirecting to an external identity provider for signout.
    The newly created ``logoutId`` would need to be round-tripped to the external identity provider at signout time, and then used on the signout callback page in the same way it would be on the normal logout page.

``GrantConsentAsync``
    Accepts a ``ConsentResponse`` to inform IdentityServer of the user's consent to a particular ``AuthorizationRequest``.

``DenyAuthorizationAsync``
    Accepts a ``AuthorizationError`` to inform IdentityServer of the error to return to the client for a particular ``AuthorizationRequest``.

``GetAllUserGrantsAsync``
    Returns a collection of ``Grant`` for the user. These represent a user's consent or a clients access to a user's resource.

``RevokeUserConsentAsync``
    Revokes all of a user's consents and grants for a client.

``RevokeTokensForCurrentSessionAsync``
    Revokes all of a user's consents and grants for clients the user has signed into during their current session.

AuthorizationRequest
^^^^^^^^^^^^^^^^^^^^
``Client``
    The client that initiated the request.
``RedirectUri``
    The URI to redirect the user to after successful authorization.
``DisplayMode``
    The display mode passed from the authorization request.
``UiLocales``
    The UI locales passed from the authorization request.
``IdP``
    The external identity provider requested.
    This is used to bypass home realm discovery (HRD).
    This is provided via the "idp:" prefix to the ``acr_values`` parameter on the authorize request.
``Tenant``
    The tenant requested.
    This is provided via the "tenant:" prefix to the ``acr_values`` parameter on the authorize request.
``LoginHint``
    The expected username the user will use to login.
    This is requested from the client via the ``login_hint`` parameter on the authorize request.
``PromptMode``
    The prompt mode requested from the authorization request.
``AcrValues``
    The acr values passed from the authorization request.
``ValidatedResources``
    The ``ResourceValidationResult`` which represents the validated resources from the authorization request.
``Parameters``
    The entire parameter collection passed to the authorization request.
``RequestObjectValues``
    The validated contents of the request object (if present).

ResourceValidationResult
^^^^^^^^^^^^^^^^^^^^^^^^
``Resources``
    The resources of the result.
``ParsedScopes``
    The parsed scopes represented by the result.
``RawScopeValues``
    The original (raw) scope values represented by the validated result.

ErrorMessage
^^^^^^^^^^^^
``DisplayMode``
    The display mode passed from the authorization request.
``UiLocales``
    The UI locales passed from the authorization request.
``Error``
    The error code.
``RequestId``
    The per-request identifier. This can be used to display to the end user and can be used in diagnostics.

LogoutRequest
^^^^^^^^^^^^^
``ClientId``
    The client identifier that initiated the request.
``PostLogoutRedirectUri``
    The URL to redirect the user to after they have logged out.
``SessionId``
    The user's current session id.
``SignOutIFrameUrl``
    The URL to render in an ``
    }


@section scripts
{
    @if (Model.AutomaticRedirectAfterSignOut)
    {
        
    }
}


================================================
FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Account/Login.cshtml
================================================
@model LoginViewModel



================================================
FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Account/Logout.cshtml
================================================
@model LogoutViewModel

Logout

Would you like to logout of IdentityServer?

================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Device/Success.cshtml ================================================

Success

You have successfully authorized the device

================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Device/UserCodeCapture.cshtml ================================================ @model string

User Code

Please enter the code displayed on your device.

================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
@if (Model.ClientLogoUrl != null) { }

@Model.ClientName is requesting your permission

@if (Model.ConfirmUserCode) {

Please confirm that the authorization request quotes the code: @Model.UserCode.

}

Uncheck the permissions you do not wish to grant.

@if (Model.IdentityScopes.Any()) {
Personal Information
    @foreach (var scope in Model.IdentityScopes) { }
} @if (Model.ApiScopes.Any()) {
Application Access
    @foreach (var scope in Model.ApiScopes) { }
}
Description
@if (Model.AllowRememberConsent) {
}
@if (Model.ClientUrl != null) { @Model.ClientName }
================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

Authentication Cookie

Claims

@foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
@claim.Type
@claim.Value
}

Properties

@foreach (var prop in Model.AuthenticateResult.Properties.Items) {
@prop.Key
@prop.Value
} @if (Model.Clients.Any()) {
Clients
@{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
}
================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

Client Application Permissions

Below is the list of applications you have given permission to and the resources they have access to.

@if (Model.Grants.Any() == false) {
You have not given access to any applications
} else { foreach (var grant in Model.Grants) {
@if (grant.ClientLogoUrl != null) { } @grant.ClientName
    @if (grant.Description != null) {
  • @grant.Description
  • }
  • @grant.Created.ToString("yyyy-MM-dd")
  • @if (grant.Expires.HasValue) {
  • @grant.Expires.Value.ToString("yyyy-MM-dd")
  • } @if (grant.IdentityGrantNames.Any()) {
    • @foreach (var name in grant.IdentityGrantNames) {
    • @name
    • }
  • } @if (grant.ApiGrantNames.Any()) {
    • @foreach (var name in grant.ApiGrantNames) {
    • @name
    • }
  • }
} }
================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @{ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); }

Welcome to IdentityServer8 (version @version)

================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

Error

Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
@errorDescription
} }
@if (request_id != null) {
Request Id: @request_id
}
================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

You are now being returned to the application

Once complete, you may close this tab.

================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
@RenderBody()
@RenderSection("scripts", required: false) ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/wwwroot/js/signin-redirect.js ================================================ //window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/IdentityServer/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Controllers/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class HomeController : Controller { private readonly ILogger _logger; public HomeController(ILogger logger) { _logger = logger; } public IActionResult Index() { return View(); } public async Task CallApi() { var accessToken = await HttpContext.GetTokenAsync("access_token"); var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var content = await client.GetStringAsync("https://localhost:6001/identity"); var obj = JsonSerializer.Deserialize(content); ViewBag.Json = obj.ToString(); return View("json"); } public IActionResult Logout() { return SignOut("Cookies", "oidc"); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Mvc; global using System.Diagnostics; global using System.IdentityModel.Tokens.Jwt; global using System.Net.Http.Headers; global using System.Text.Json; ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Models/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class ErrorViewModel { public string RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/MvcClient.csproj ================================================ true PreserveNewest ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ JwtSecurityTokenHandler.DefaultMapInboundClaims = false; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); builder.Services .AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://localhost:5001"; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.Scope.Add("api1"); options.SaveTokens = true; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); else app.UseExceptionHandler("/Home/Error"); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapDefaultControllerRoute().RequireAuthorization(); await app.RunAsync(); } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Properties/launchSettings.json ================================================ { "profiles": { "MvcClient": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5002" } } } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Views/Home/Index.cshtml ================================================ @using Microsoft.AspNetCore.Authentication

    Claims

    @foreach (var claim in User.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items) {
    @prop.Key
    @prop.Value
    }
    ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Views/Home/Privacy.cshtml ================================================ @{ ViewData["Title"] = "Privacy Policy"; }

    @ViewData["Title"]

    Use this page to detail your site's privacy policy.

    ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ ViewData["Title"] = "Error"; }

    Error.

    An error occurred while processing your request.

    @if (Model.ShowRequestId) {

    Request ID: @Model.RequestId

    }

    Development Mode

    Swapping to Development environment will display more detailed information about the error that occurred.

    The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app.

    ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Views/Shared/_Layout.cshtml ================================================ @ViewData["Title"] - MvcClient
    @RenderBody()
    @RenderSection("Scripts", required: false) ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Views/Shared/_ValidationScriptsPartial.cshtml ================================================  ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Views/Shared/json.cshtml ================================================
    @ViewBag.Json
    ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Views/_ViewImports.cshtml ================================================ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/appsettings.json ================================================ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/wwwroot/css/site.css ================================================ /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification for details on configuring this project to bundle and minify static web assets. */ a.navbar-brand { white-space: normal; text-align: center; word-break: break-all; } /* Provide sufficient contrast against white background */ a { color: #0366d6; } .btn-primary { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } /* Sticky footer styles -------------------------------------------------- */ html { font-size: 14px; } @media (min-width: 768px) { html { font-size: 16px; } } .border-top { border-top: 1px solid #e5e5e5; } .border-bottom { border-bottom: 1px solid #e5e5e5; } .box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); } button.accept-policy { font-size: 1rem; line-height: inherit; } /* Sticky footer styles -------------------------------------------------- */ html { position: relative; min-height: 100%; } body { /* Margin bottom by footer height */ margin-bottom: 60px; } .footer { position: absolute; bottom: 0; width: 100%; white-space: nowrap; line-height: 60px; /* Vertically center the text there */ } ================================================ FILE: samples/Quickstarts/2_InteractiveAspNetCore/src/MvcClient/wwwroot/js/site.js ================================================ // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification // for details on configuring this project to bundle and minify static web assets. // Write your JavaScript code. ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/Quickstart.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34322.80 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BD8BA25C-A91D-419D-99B6-B575ADD6D061}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer", "src\IdentityServer\IdentityServer.csproj", "{79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvcClient", "src\MvcClient\MvcClient.csproj", "{BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api", "..\Shared\src\Api\Api.csproj", "{FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "..\Shared\src\Client\Client.csproj", "{78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|Any CPU.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x64.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x64.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x86.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x86.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|Any CPU.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|Any CPU.Build.0 = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x64.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x64.Build.0 = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x86.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x86.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|Any CPU.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x64.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x64.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x86.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x86.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|Any CPU.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x64.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x64.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x86.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x86.Build.0 = Release|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Debug|Any CPU.Build.0 = Debug|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Debug|x64.ActiveCfg = Debug|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Debug|x64.Build.0 = Debug|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Debug|x86.ActiveCfg = Debug|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Debug|x86.Build.0 = Debug|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Release|Any CPU.ActiveCfg = Release|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Release|Any CPU.Build.0 = Release|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Release|x64.ActiveCfg = Release|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Release|x64.Build.0 = Release|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Release|x86.ActiveCfg = Release|Any CPU {FB7DDF43-1EB6-4FBF-B83D-6E19F723D7E4}.Release|x86.Build.0 = Release|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Debug|Any CPU.Build.0 = Debug|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Debug|x64.ActiveCfg = Debug|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Debug|x64.Build.0 = Debug|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Debug|x86.ActiveCfg = Debug|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Debug|x86.Build.0 = Debug|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Release|Any CPU.ActiveCfg = Release|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Release|Any CPU.Build.0 = Release|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Release|x64.ActiveCfg = Release|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Release|x64.Build.0 = Release|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Release|x86.ActiveCfg = Release|Any CPU {78C0AEF8-E8B7-4F62-948C-FEFBF0ED881B}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {84D5AF06-1243-46F1-9D58-70ED572832C3} EndGlobalSection EndGlobal ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/Quickstart.sln.licenseheader ================================================ extensions: designer.cs generated.cs extensions: .cs /* Copyright (c) 2024 HigginsSoft Written by Alexander Higgins https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code for this software can be found at https://github.com/alexhiggins732/IdentityServer8 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Config.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Secret = IdentityServer8.Models.Secret; public static class Config { public static IEnumerable IdentityResources => new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable ApiScopes => new List { new ApiScope("api1", "My API") }; public static IEnumerable Clients => new List { // machine to machine client new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, // scopes that client has access to AllowedScopes = { "api1" } }, // interactive ASP.NET Core MVC client new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // where to redirect to after login RedirectUris = { "https://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } } }; } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using IdentityServerHost.Quickstart.UI; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Hosting; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Options; global using Microsoft.IdentityModel.Tokens; global using Serilog; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System; global using System.ComponentModel.DataAnnotations; global using System.Security.Claims; global using System.Text; global using System.Text.Json; global using static IdentityModel.JwtClaimTypes; global using IdentityServerClaimValueTypes = IdentityServer8.IdentityServerConstants.ClaimValueTypes; ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/IdentityServer.csproj ================================================ ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ConfigureLogger(); try { Log.Information("Starting host..."); var builder = WebApplication.CreateBuilder(args); var services = builder.Services; services.AddControllersWithViews(); services .AddIdentityServer() .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users) .AddDeveloperSigningCredential(); services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = ""; options.ClientSecret = ""; }) .AddOpenIdConnect("oidc", "Demo IdentityServer", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.SaveTokens = true; options.Authority = "https://demo.identityserver8.io/"; options.ClientId = "interactive.confidential"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.TokenValidationParameters = new() { NameClaimType = "name", RoleClaimType = "role" }; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseStaticFiles() .UseRouting() .UseIdentityServer() .UseAuthorization(); app.MapDefaultControllerRoute(); await app.RunAsync(); } return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } void ConfigureLogger() => Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() // uncomment to write to Azure diagnostics stream //.WriteTo.File( // @"D:\home\LogFiles\Application\identityserver.txt", // fileSizeLimitBytes: 1_000_000, // rollOnFileSizeLimit: true, // shared: true, // flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) .CreateLogger(); ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Properties/launchSettings.json ================================================ { "profiles": { "SelfHost": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001" } } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/AccountController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval /// [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController( IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; } /// /// Entry point into the login workflow /// [HttpGet] public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); if (vm.IsExternalLoginOnly) { // we only have one option for logging in and it's an external provider return returnUrl.IsAllowedRedirect() ? RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl = returnUrl.SanitizeForRedirect() }) : Forbid(); } return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (ModelState.IsValid) { // validate username/password against in-memory store if (_users.ValidateCredentials(model.Username, model.Password)) { var user = _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.Client.ClientId)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username }; await HttpContext.SignInAsync(isuser, props); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task LoginCancel(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else { // since we don't have a valid context, then we just go back to the home page return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } } /// /// Show logout page /// [HttpGet] public async Task Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); } /// /// Handle logout page postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await HttpContext.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); } [HttpGet] public IActionResult AccessDenied() { return View(); } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) { var local = context.IdP == IdentityServer8.IdentityServerConstants.LocalIdentityProvider; // this is meant to short circuit the UI and only trigger the one external IdP var vm = new LoginViewModel { EnableLocalLogin = local, ReturnUrl = returnUrl, Username = context?.LoginHint, }; if (!local) { vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; } return vm; } var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes .Where(x => x.DisplayName != null) .Select(x => new ExternalProvider { DisplayName = x.DisplayName ?? x.Name, AuthenticationScheme = x.Name }).ToList(); var allowLocal = true; if (context?.Client.ClientId != null) { var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); if (client != null) { allowLocal = client.EnableLocalLogin; if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) { providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); } } } return new LoginViewModel { AllowRememberLogin = AccountOptions.AllowRememberLogin, EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, ReturnUrl = returnUrl, Username = context?.LoginHint, ExternalProviders = providers.ToArray() }; } private async Task BuildLoginViewModelAsync(LoginInputModel model) { var vm = await BuildLoginViewModelAsync(model.ReturnUrl); vm.Username = model.Username; vm.RememberLogin = model.RememberLogin; return vm; } private async Task BuildLogoutViewModelAsync(string logoutId) { var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; if (User?.Identity.IsAuthenticated != true) { // if the user is not authenticated, then just show logged out page vm.ShowLogoutPrompt = false; return vm; } var context = await _interaction.GetLogoutContextAsync(logoutId); if (context?.ShowSignoutPrompt == false) { // it's safe to automatically sign-out vm.ShowLogoutPrompt = false; return vm; } // show the logout prompt. this prevents attacks where the user // is automatically signed out by another malicious web page. return vm; } private async Task BuildLoggedOutViewModelAsync(string logoutId) { // get context information (client name, post logout redirect URI and iframe for federated signout) var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LoggedOutViewModel { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; if (User?.Identity.IsAuthenticated == true) { var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; if (idp != null && idp != IdentityServer8.IdentityServerConstants.LocalIdentityProvider) { var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); if (providerSupportsSignout) { if (vm.LogoutId == null) { // if there's no current logout context, we need to create one // this captures necessary info from the current logged in user // before we signout and redirect away to the external IdP for signout vm.LogoutId = await _interaction.CreateLogoutContextAsync(); } vm.ExternalAuthenticationScheme = idp; } } } return vm; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/AccountOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI { public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/ExternalController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly ILogger _logger; private readonly IEventService _events; public ExternalController( IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService events, ILogger logger, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _logger = logger; _events = events; } /// /// initiate roundtrip to external authentication provider /// [HttpGet] public IActionResult Challenge(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; // validate returnUrl - either it is a valid OIDC URL or back to a local page if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } // start challenge and roundtrip the return URL and scheme var props = new AuthenticationProperties { RedirectUri = Url.Action(nameof(Callback)), Items = { { "returnUrl", returnUrl }, { "scheme", scheme }, } }; return Challenge(props, scheme); } /// /// Post processing of external authentication /// [HttpGet] public async Task Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = AutoProvisionUser(provider, providerUserId, claims); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); return (user, provider, providerUserId, claims); } private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) { var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); return user; } // if the external login is OIDC-based, there are certain things we need to preserve to make logout work // this will be different for WS-Fed, SAML2p or other protocols private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) { // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout var idToken = externalResult.Properties.GetTokenValue("id_token"); if (idToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); } } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/ExternalProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ExternalProvider { public string DisplayName { get; set; } public string AuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/LoggedOutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoggedOutViewModel { public string PostLogoutRedirectUri { get; set; } public string ClientName { get; set; } public string SignOutIframeUrl { get; set; } public bool AutomaticRedirectAfterSignOut { get; set; } public string LogoutId { get; set; } public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; public string ExternalAuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/LoginInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginInputModel { [Required] public string Username { get; set; } [Required] public string Password { get; set; } public bool RememberLogin { get; set; } public string ReturnUrl { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginViewModel : LoginInputModel { public bool AllowRememberLogin { get; set; } = true; public bool EnableLocalLogin { get; set; } = true; public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/LogoutInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutInputModel { public string LogoutId { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/LogoutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutViewModel : LogoutInputModel { public bool ShowLogoutPrompt { get; set; } = true; } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Account/RedirectViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class RedirectViewModel { public string RedirectUrl { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Consent/ConsentController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This controller processes the consent UI /// [SecurityHeaders] [Authorize] public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IEventService _events; private readonly ILogger _logger; public ConsentController( IIdentityServerInteractionService interaction, IEventService events, ILogger logger) { _interaction = interaction; _events = events; _logger = logger; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { var result = await ProcessConsent(model); if (result.IsRedirect) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context?.IsNativeClient() == true) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", result.RedirectUri); } return result.RedirectUri.IsAllowedRedirect() ? Redirect(result.RedirectUri.SanitizeForRedirect()) : Forbid(); } if (result.HasValidationError) { ModelState.AddModelError(string.Empty, result.ValidationError); } if (result.ShowView) { return View("Index", result.ViewModel); } return View("Error"); } /*****************************************/ /* helper APIs for the ConsentController */ /*****************************************/ private async Task ProcessConsent(ConsentInputModel model) { var result = new ProcessConsentResult(); // validate return url is still valid var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model?.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model?.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); } return result; } private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { return CreateConsentViewModel(model, returnUrl, request); } else { _logger.LogError("No consent request matching request: {0}", returnUrl.SanitizeForLog()); } return null; } private ConsentViewModel CreateConsentViewModel( ConsentInputModel model, string returnUrl, AuthorizationRequest request) { var vm = new ConsentViewModel { RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), Description = model?.Description, ReturnUrl = returnUrl, ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach(var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { var displayName = apiScope.DisplayName ?? apiScope.Name; if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) { displayName += ":" + parsedScopeValue.ParsedParameter; } return new ScopeViewModel { Value = parsedScopeValue.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Consent/ConsentInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentInputModel { public string Button { get; set; } public IEnumerable ScopesConsented { get; set; } public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Consent/ConsentOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentOptions { public static bool EnableOfflineAccess = true; public static string OfflineAccessDisplayName = "Offline Access"; public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Consent/ConsentViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentViewModel : ConsentInputModel { public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public bool AllowRememberConsent { get; set; } public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Consent/ProcessConsentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ProcessConsentResult { public bool IsRedirect => RedirectUri != null; public string RedirectUri { get; set; } public Client Client { get; set; } public bool ShowView => ViewModel != null; public ConsentViewModel ViewModel { get; set; } public bool HasValidationError => ValidationError != null; public string ValidationError { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Consent/ScopeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ScopeViewModel { public string Value { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Emphasize { get; set; } public bool Required { get; set; } public bool Checked { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationViewModel : ConsentViewModel { public string UserCode { get; set; } public bool ConfirmUserCode { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Device/DeviceController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [Authorize] [SecurityHeaders] public class DeviceController : Controller { private readonly IDeviceFlowInteractionService _interaction; private readonly IEventService _events; private readonly IOptions _options; private readonly ILogger _logger; public DeviceController( IDeviceFlowInteractionService interaction, IEventService eventService, IOptions options, ILogger logger) { _interaction = interaction; _events = eventService; _options = options; _logger = logger; } [HttpGet] public async Task Index() { string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; string userCode = Request.Query[userCodeParamName]; if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); vm.ConfirmUserCode = true; return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task UserCodeCapture(string userCode) { var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task Callback(DeviceAuthorizationInputModel model) { if (model == null) throw new ArgumentNullException(nameof(model)); var result = await ProcessConsent(model); if (result.HasValidationError) return View("Error"); return View("Success"); } private async Task ProcessConsent(DeviceAuthorizationInputModel model) { var result = new ProcessConsentResult(); var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.UserCode, model); } return result; } private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(userCode); if (request != null) { return CreateConsentViewModel(userCode, model, request); } return null; } private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { UserCode = userCode, Description = model?.Description, RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? DisplayName = apiScope.DisplayName ?? apiScope.Name, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [Authorize] public class DiagnosticsController : Controller { public async Task Index() { var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) { return NotFound(); } var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); return View(model); } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DiagnosticsViewModel { public DiagnosticsViewModel(AuthenticateResult result) { AuthenticateResult = result; if (result.Properties.Items.ContainsKey("client_list")) { var encoded = result.Properties.Items["client_list"]; var bytes = Base64Url.Decode(encoded); var value = Encoding.UTF8.GetString(bytes); Clients = JsonSerializer.Deserialize(value); } } public AuthenticateResult AuthenticateResult { get; } public IEnumerable Clients { get; } = new List(); } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public static class Extensions { /// /// Checks if the redirect URI is for a native client. /// /// public static bool IsNativeClient(this AuthorizationRequest context) { return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); } public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) { controller.HttpContext.Response.StatusCode = 200; controller.HttpContext.Response.Headers["Location"] = ""; return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Grants/GrantsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller allows a user to revoke grants given to clients /// [SecurityHeaders] [Authorize] public class GrantsController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; private readonly IResourceStore _resources; private readonly IEventService _events; public GrantsController(IIdentityServerInteractionService interaction, IClientStore clients, IResourceStore resources, IEventService events) { _interaction = interaction; _clients = clients; _resources = resources; _events = events; } /// /// Show list of grants /// [HttpGet] public async Task Index() { return View("Index", await BuildViewModelAsync()); } /// /// Handle postback to revoke a client /// [HttpPost] [ValidateAntiForgeryToken] public async Task Revoke(string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return RedirectToAction("Index"); } private async Task BuildViewModelAsync() { var grants = await _interaction.GetAllUserGrantsAsync(); var list = new List(); foreach(var grant in grants) { var client = await _clients.FindClientByIdAsync(grant.ClientId); if (client != null) { var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); var item = new GrantViewModel() { ClientId = client.ClientId, ClientName = client.ClientName ?? client.ClientId, ClientLogoUrl = client.LogoUri, ClientUrl = client.ClientUri, Description = grant.Description, Created = grant.CreationTime, Expires = grant.Expiration, IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() }; list.Add(item); } } return new GrantsViewModel { Grants = list }; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Grants/GrantsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class GrantsViewModel { public IEnumerable Grants { get; set; } } public class GrantViewModel { public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public string Description { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Home/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ErrorViewModel { public ErrorViewModel() { } public ErrorViewModel(string error) { Error = new ErrorMessage { Error = error }; } public ErrorMessage Error { get; set; } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/Home/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) { _interaction = interaction; _environment = environment; _logger = logger; } public IActionResult Index() { if (_environment.IsDevelopment()) { // only show in development return View(); } _logger.LogInformation("Homepage is disabled in production. Returning 404."); return NotFound(); } /// /// Shows the error page /// public async Task Error(string errorId) { var vm = new ErrorViewModel(); // retrieve error details from identityserver var message = await _interaction.GetErrorContextAsync(errorId); if (message != null) { vm.Error = message; if (!_environment.IsDevelopment()) { // only show in development message.ErrorDescription = null; } } return View("Error", vm); } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/SecurityHeadersAttribute.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class SecurityHeadersAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { var result = context.Result; if (result is ViewResult) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) { context.HttpContext.Response.Headers.Append("X-Content-Type-Options", "nosniff"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) { context.HttpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; // also consider adding upgrade-insecure-requests once you have HTTPS in place for production //csp += "upgrade-insecure-requests;"; // also an example if you need client images to be displayed from twitter // csp += "img-src 'self' https://pbs.twimg.com;"; // once for standards compliant browsers if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("Content-Security-Policy", csp); } // and once again for IE if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("X-Content-Security-Policy", csp); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy var referrer_policy = "no-referrer"; if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) { context.HttpContext.Response.Headers.Append("Referrer-Policy", referrer_policy); } } } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Quickstart/TestUsers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class TestUsers { static readonly object UserAddress = new { street_address = "One Hacker Way", locality = "Heidelberg", postal_code = 69118, country = "Germany" }; public static List Users => new List { new() { SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new (Name, "Alice Smith"), new (GivenName, "Alice"), new (FamilyName, "Smith"), new (Email, "AliceSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://alice.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } }, new() { SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new (Name, "Bob Smith"), new (GivenName, "Bob"), new (FamilyName, "Smith"), new (Email, "BobSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://bob.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } } }; } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Account/AccessDenied.cshtml ================================================ 

    Access Denied

    You do not have access to that resource.

    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Account/LoggedOut.cshtml ================================================ @model LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user ViewData["signed-out"] = true; }

    Logout You are now logged out

    @if (Model.PostLogoutRedirectUri != null) {
    Click here to return to the @Model.ClientName application.
    } @if (Model.SignOutIframeUrl != null) { }
    @section scripts { @if (Model.AutomaticRedirectAfterSignOut) { } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Account/Login.cshtml ================================================ @model LoginViewModel ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Account/Logout.cshtml ================================================ @model LogoutViewModel

    Logout

    Would you like to logout of IdentityServer?

    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Device/Success.cshtml ================================================

    Success

    You have successfully authorized the device

    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Device/UserCodeCapture.cshtml ================================================ @model string

    User Code

    Please enter the code displayed on your device.

    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
    @if (Model.ClientLogoUrl != null) { }

    @Model.ClientName is requesting your permission

    @if (Model.ConfirmUserCode) {

    Please confirm that the authorization request quotes the code: @Model.UserCode.

    }

    Uncheck the permissions you do not wish to grant.

    @if (Model.IdentityScopes.Any()) {
    Personal Information
      @foreach (var scope in Model.IdentityScopes) { }
    } @if (Model.ApiScopes.Any()) {
    Application Access
      @foreach (var scope in Model.ApiScopes) { }
    }
    Description
    @if (Model.AllowRememberConsent) {
    }
    @if (Model.ClientUrl != null) { @Model.ClientName }
    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

    Authentication Cookie

    Claims

    @foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in Model.AuthenticateResult.Properties.Items) {
    @prop.Key
    @prop.Value
    } @if (Model.Clients.Any()) {
    Clients
    @{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
    }
    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

    Client Application Permissions

    Below is the list of applications you have given permission to and the resources they have access to.

    @if (Model.Grants.Any() == false) {
    You have not given access to any applications
    } else { foreach (var grant in Model.Grants) {
    @if (grant.ClientLogoUrl != null) { } @grant.ClientName
      @if (grant.Description != null) {
    • @grant.Description
    • }
    • @grant.Created.ToString("yyyy-MM-dd")
    • @if (grant.Expires.HasValue) {
    • @grant.Expires.Value.ToString("yyyy-MM-dd")
    • } @if (grant.IdentityGrantNames.Any()) {
      • @foreach (var name in grant.IdentityGrantNames) {
      • @name
      • }
    • } @if (grant.ApiGrantNames.Any()) {
      • @foreach (var name in grant.ApiGrantNames) {
      • @name
      • }
    • }
    } }
    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @{ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); }

    Welcome to IdentityServer8 (version @version)

    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

    Error

    Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
    @errorDescription
    } }
    @if (request_id != null) {
    Request Id: @request_id
    }
    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

    You are now being returned to the application

    Once complete, you may close this tab.

    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
    @RenderBody()
    @RenderSection("scripts", required: false) ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/wwwroot/js/signin-redirect.js ================================================ //window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/IdentityServer/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Controllers/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class HomeController : Controller { private readonly ILogger _logger; public HomeController(ILogger logger) { _logger = logger; } public IActionResult Index() { return View(); } public async Task CallApi() { var accessToken = await HttpContext.GetTokenAsync("access_token"); var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var content = await client.GetStringAsync("https://localhost:6001/identity"); var obj = JsonSerializer.Deserialize(content); ViewBag.Json = obj.ToString(); return View("json"); } public IActionResult Logout() { return SignOut("Cookies", "oidc"); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Mvc; global using System.Diagnostics; global using System.IdentityModel.Tokens.Jwt; global using System.Net.Http.Headers; global using System.Text.Json; ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Models/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class ErrorViewModel { public string RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/MvcClient.csproj ================================================ true PreserveNewest ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ JwtSecurityTokenHandler.DefaultMapInboundClaims = false; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); builder.Services .AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://localhost:5001"; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.Scope.Add("api1"); options.SaveTokens = true; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); else app.UseExceptionHandler("/Home/Error"); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapDefaultControllerRoute().RequireAuthorization(); await app.RunAsync(); } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Properties/launchSettings.json ================================================ { "profiles": { "MvcClient": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5002" } } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Views/Home/Index.cshtml ================================================ @using Microsoft.AspNetCore.Authentication

    Claims

    @foreach (var claim in User.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items) {
    @prop.Key
    @prop.Value
    }
    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Views/Home/Privacy.cshtml ================================================ @{ ViewData["Title"] = "Privacy Policy"; }

    @ViewData["Title"]

    Use this page to detail your site's privacy policy.

    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ ViewData["Title"] = "Error"; }

    Error.

    An error occurred while processing your request.

    @if (Model.ShowRequestId) {

    Request ID: @Model.RequestId

    }

    Development Mode

    Swapping to Development environment will display more detailed information about the error that occurred.

    The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app.

    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Views/Shared/_Layout.cshtml ================================================ @ViewData["Title"] - MvcClient
    @RenderBody()
    @RenderSection("Scripts", required: false) ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Views/Shared/_ValidationScriptsPartial.cshtml ================================================  ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Views/Shared/json.cshtml ================================================
    @ViewBag.Json
    ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Views/_ViewImports.cshtml ================================================ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/appsettings.json ================================================ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/wwwroot/css/site.css ================================================ /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification for details on configuring this project to bundle and minify static web assets. */ a.navbar-brand { white-space: normal; text-align: center; word-break: break-all; } /* Provide sufficient contrast against white background */ a { color: #0366d6; } .btn-primary { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } /* Sticky footer styles -------------------------------------------------- */ html { font-size: 14px; } @media (min-width: 768px) { html { font-size: 16px; } } .border-top { border-top: 1px solid #e5e5e5; } .border-bottom { border-bottom: 1px solid #e5e5e5; } .box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); } button.accept-policy { font-size: 1rem; line-height: inherit; } /* Sticky footer styles -------------------------------------------------- */ html { position: relative; min-height: 100%; } body { /* Margin bottom by footer height */ margin-bottom: 60px; } .footer { position: absolute; bottom: 0; width: 100%; white-space: nowrap; line-height: 60px; /* Vertically center the text there */ } ================================================ FILE: samples/Quickstarts/3_AspNetCoreAndApis/src/MvcClient/wwwroot/js/site.js ================================================ // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification // for details on configuring this project to bundle and minify static web assets. // Write your JavaScript code. ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/Quickstart.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34322.80 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BD8BA25C-A91D-419D-99B6-B575ADD6D061}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer", "src\IdentityServer\IdentityServer.csproj", "{79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvcClient", "src\MvcClient\MvcClient.csproj", "{BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "JavaScriptClient", "src\JavaScriptClient\JavaScriptClient.csproj", "{DD8D4022-0073-40EE-84F0-8E4833522BB9}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api", "..\Shared\src\Api\Api.csproj", "{8D0EA8BB-7B55-4F60-8011-EF220103BADB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "..\Shared\src\Client\Client.csproj", "{8FC8CFB9-740B-4D13-B396-99283AA758AC}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|Any CPU.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x64.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x64.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x86.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x86.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|Any CPU.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|Any CPU.Build.0 = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x64.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x64.Build.0 = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x86.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x86.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|Any CPU.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x64.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x64.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x86.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x86.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|Any CPU.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x64.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x64.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x86.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x86.Build.0 = Release|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Debug|x64.ActiveCfg = Debug|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Debug|x64.Build.0 = Debug|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Debug|x86.ActiveCfg = Debug|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Debug|x86.Build.0 = Debug|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Release|Any CPU.Build.0 = Release|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Release|x64.ActiveCfg = Release|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Release|x64.Build.0 = Release|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Release|x86.ActiveCfg = Release|Any CPU {DD8D4022-0073-40EE-84F0-8E4833522BB9}.Release|x86.Build.0 = Release|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Debug|Any CPU.Build.0 = Debug|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Debug|x64.ActiveCfg = Debug|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Debug|x64.Build.0 = Debug|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Debug|x86.ActiveCfg = Debug|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Debug|x86.Build.0 = Debug|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Release|Any CPU.ActiveCfg = Release|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Release|Any CPU.Build.0 = Release|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Release|x64.ActiveCfg = Release|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Release|x64.Build.0 = Release|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Release|x86.ActiveCfg = Release|Any CPU {8D0EA8BB-7B55-4F60-8011-EF220103BADB}.Release|x86.Build.0 = Release|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Debug|Any CPU.Build.0 = Debug|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Debug|x64.ActiveCfg = Debug|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Debug|x64.Build.0 = Debug|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Debug|x86.ActiveCfg = Debug|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Debug|x86.Build.0 = Debug|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Release|Any CPU.ActiveCfg = Release|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Release|Any CPU.Build.0 = Release|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Release|x64.ActiveCfg = Release|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Release|x64.Build.0 = Release|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Release|x86.ActiveCfg = Release|Any CPU {8FC8CFB9-740B-4D13-B396-99283AA758AC}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} {DD8D4022-0073-40EE-84F0-8E4833522BB9} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {84D5AF06-1243-46F1-9D58-70ED572832C3} EndGlobalSection EndGlobal ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/Quickstart.sln.licenseheader ================================================ extensions: designer.cs generated.cs extensions: .cs /* Copyright (c) 2024 HigginsSoft Written by Alexander Higgins https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code for this software can be found at https://github.com/alexhiggins732/IdentityServer8 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Config.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Secret = IdentityServer8.Models.Secret; public static class Config { public static IEnumerable IdentityResources => new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable ApiScopes => new List { new ApiScope("api1", "My API") }; public static IEnumerable Clients => new List { // machine to machine client new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, // scopes that client has access to AllowedScopes = { "api1" } }, // interactive ASP.NET Core MVC client new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // where to redirect to after login RedirectUris = { "https://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } } }; } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using IdentityServerHost.Quickstart.UI; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Hosting; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Options; global using Microsoft.IdentityModel.Tokens; global using Serilog; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System; global using System.ComponentModel.DataAnnotations; global using System.Security.Claims; global using System.Text; global using System.Text.Json; global using static IdentityModel.JwtClaimTypes; global using IdentityServerClaimValueTypes = IdentityServer8.IdentityServerConstants.ClaimValueTypes; ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/IdentityServer.csproj ================================================ ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ConfigureLogger(); try { Log.Information("Starting host..."); var builder = WebApplication.CreateBuilder(args); var services = builder.Services; services.AddControllersWithViews(); services .AddIdentityServer() .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users) .AddDeveloperSigningCredential(); services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = ""; options.ClientSecret = ""; }) .AddOpenIdConnect("oidc", "Demo IdentityServer", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.SaveTokens = true; options.Authority = "https://demo.identityserver8.io/"; options.ClientId = "interactive.confidential"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.TokenValidationParameters = new() { NameClaimType = "name", RoleClaimType = "role" }; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseStaticFiles() .UseRouting() .UseIdentityServer() .UseAuthorization(); app.MapDefaultControllerRoute(); await app.RunAsync(); } return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } void ConfigureLogger() => Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() // uncomment to write to Azure diagnostics stream //.WriteTo.File( // @"D:\home\LogFiles\Application\identityserver.txt", // fileSizeLimitBytes: 1_000_000, // rollOnFileSizeLimit: true, // shared: true, // flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) .CreateLogger(); ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Properties/launchSettings.json ================================================ { "profiles": { "SelfHost": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001" } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/AccountController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval /// [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController( IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; } /// /// Entry point into the login workflow /// [HttpGet] public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); if (vm.IsExternalLoginOnly) { // we only have one option for logging in and it's an external provider return returnUrl.IsAllowedRedirect() ? RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl = returnUrl.SanitizeForRedirect() }) : Forbid(); } return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (ModelState.IsValid) { // validate username/password against in-memory store if (_users.ValidateCredentials(model.Username, model.Password)) { var user = _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.Client.ClientId)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username }; await HttpContext.SignInAsync(isuser, props); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task LoginCancel(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else { // since we don't have a valid context, then we just go back to the home page return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } } /// /// Show logout page /// [HttpGet] public async Task Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); } /// /// Handle logout page postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await HttpContext.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); } [HttpGet] public IActionResult AccessDenied() { return View(); } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) { var local = context.IdP == IdentityServer8.IdentityServerConstants.LocalIdentityProvider; // this is meant to short circuit the UI and only trigger the one external IdP var vm = new LoginViewModel { EnableLocalLogin = local, ReturnUrl = returnUrl, Username = context?.LoginHint, }; if (!local) { vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; } return vm; } var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes .Where(x => x.DisplayName != null) .Select(x => new ExternalProvider { DisplayName = x.DisplayName ?? x.Name, AuthenticationScheme = x.Name }).ToList(); var allowLocal = true; if (context?.Client.ClientId != null) { var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); if (client != null) { allowLocal = client.EnableLocalLogin; if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) { providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); } } } return new LoginViewModel { AllowRememberLogin = AccountOptions.AllowRememberLogin, EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, ReturnUrl = returnUrl, Username = context?.LoginHint, ExternalProviders = providers.ToArray() }; } private async Task BuildLoginViewModelAsync(LoginInputModel model) { var vm = await BuildLoginViewModelAsync(model.ReturnUrl); vm.Username = model.Username; vm.RememberLogin = model.RememberLogin; return vm; } private async Task BuildLogoutViewModelAsync(string logoutId) { var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; if (User?.Identity.IsAuthenticated != true) { // if the user is not authenticated, then just show logged out page vm.ShowLogoutPrompt = false; return vm; } var context = await _interaction.GetLogoutContextAsync(logoutId); if (context?.ShowSignoutPrompt == false) { // it's safe to automatically sign-out vm.ShowLogoutPrompt = false; return vm; } // show the logout prompt. this prevents attacks where the user // is automatically signed out by another malicious web page. return vm; } private async Task BuildLoggedOutViewModelAsync(string logoutId) { // get context information (client name, post logout redirect URI and iframe for federated signout) var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LoggedOutViewModel { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; if (User?.Identity.IsAuthenticated == true) { var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; if (idp != null && idp != IdentityServer8.IdentityServerConstants.LocalIdentityProvider) { var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); if (providerSupportsSignout) { if (vm.LogoutId == null) { // if there's no current logout context, we need to create one // this captures necessary info from the current logged in user // before we signout and redirect away to the external IdP for signout vm.LogoutId = await _interaction.CreateLogoutContextAsync(); } vm.ExternalAuthenticationScheme = idp; } } } return vm; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/AccountOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI { public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/ExternalController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly ILogger _logger; private readonly IEventService _events; public ExternalController( IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService events, ILogger logger, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _logger = logger; _events = events; } /// /// initiate roundtrip to external authentication provider /// [HttpGet] public IActionResult Challenge(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; // validate returnUrl - either it is a valid OIDC URL or back to a local page if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } // start challenge and roundtrip the return URL and scheme var props = new AuthenticationProperties { RedirectUri = Url.Action(nameof(Callback)), Items = { { "returnUrl", returnUrl }, { "scheme", scheme }, } }; return Challenge(props, scheme); } /// /// Post processing of external authentication /// [HttpGet] public async Task Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = AutoProvisionUser(provider, providerUserId, claims); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); return (user, provider, providerUserId, claims); } private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) { var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); return user; } // if the external login is OIDC-based, there are certain things we need to preserve to make logout work // this will be different for WS-Fed, SAML2p or other protocols private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) { // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout var idToken = externalResult.Properties.GetTokenValue("id_token"); if (idToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/ExternalProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ExternalProvider { public string DisplayName { get; set; } public string AuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/LoggedOutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoggedOutViewModel { public string PostLogoutRedirectUri { get; set; } public string ClientName { get; set; } public string SignOutIframeUrl { get; set; } public bool AutomaticRedirectAfterSignOut { get; set; } public string LogoutId { get; set; } public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; public string ExternalAuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/LoginInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginInputModel { [Required] public string Username { get; set; } [Required] public string Password { get; set; } public bool RememberLogin { get; set; } public string ReturnUrl { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginViewModel : LoginInputModel { public bool AllowRememberLogin { get; set; } = true; public bool EnableLocalLogin { get; set; } = true; public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/LogoutInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutInputModel { public string LogoutId { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/LogoutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutViewModel : LogoutInputModel { public bool ShowLogoutPrompt { get; set; } = true; } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Account/RedirectViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class RedirectViewModel { public string RedirectUrl { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Consent/ConsentController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This controller processes the consent UI /// [SecurityHeaders] [Authorize] public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IEventService _events; private readonly ILogger _logger; public ConsentController( IIdentityServerInteractionService interaction, IEventService events, ILogger logger) { _interaction = interaction; _events = events; _logger = logger; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { var result = await ProcessConsent(model); if (result.IsRedirect) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context?.IsNativeClient() == true) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", result.RedirectUri); } return result.RedirectUri.IsAllowedRedirect() ? Redirect(result.RedirectUri.SanitizeForRedirect()) : Forbid(); } if (result.HasValidationError) { ModelState.AddModelError(string.Empty, result.ValidationError); } if (result.ShowView) { return View("Index", result.ViewModel); } return View("Error"); } /*****************************************/ /* helper APIs for the ConsentController */ /*****************************************/ private async Task ProcessConsent(ConsentInputModel model) { var result = new ProcessConsentResult(); // validate return url is still valid var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model?.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model?.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); } return result; } private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { return CreateConsentViewModel(model, returnUrl, request); } else { _logger.LogError("No consent request matching request: {0}", returnUrl.SanitizeForLog()); } return null; } private ConsentViewModel CreateConsentViewModel( ConsentInputModel model, string returnUrl, AuthorizationRequest request) { var vm = new ConsentViewModel { RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), Description = model?.Description, ReturnUrl = returnUrl, ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach(var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { var displayName = apiScope.DisplayName ?? apiScope.Name; if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) { displayName += ":" + parsedScopeValue.ParsedParameter; } return new ScopeViewModel { Value = parsedScopeValue.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Consent/ConsentInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentInputModel { public string Button { get; set; } public IEnumerable ScopesConsented { get; set; } public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Consent/ConsentOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentOptions { public static bool EnableOfflineAccess = true; public static string OfflineAccessDisplayName = "Offline Access"; public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Consent/ConsentViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentViewModel : ConsentInputModel { public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public bool AllowRememberConsent { get; set; } public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Consent/ProcessConsentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ProcessConsentResult { public bool IsRedirect => RedirectUri != null; public string RedirectUri { get; set; } public Client Client { get; set; } public bool ShowView => ViewModel != null; public ConsentViewModel ViewModel { get; set; } public bool HasValidationError => ValidationError != null; public string ValidationError { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Consent/ScopeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ScopeViewModel { public string Value { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Emphasize { get; set; } public bool Required { get; set; } public bool Checked { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationViewModel : ConsentViewModel { public string UserCode { get; set; } public bool ConfirmUserCode { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Device/DeviceController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [Authorize] [SecurityHeaders] public class DeviceController : Controller { private readonly IDeviceFlowInteractionService _interaction; private readonly IEventService _events; private readonly IOptions _options; private readonly ILogger _logger; public DeviceController( IDeviceFlowInteractionService interaction, IEventService eventService, IOptions options, ILogger logger) { _interaction = interaction; _events = eventService; _options = options; _logger = logger; } [HttpGet] public async Task Index() { string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; string userCode = Request.Query[userCodeParamName]; if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); vm.ConfirmUserCode = true; return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task UserCodeCapture(string userCode) { var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task Callback(DeviceAuthorizationInputModel model) { if (model == null) throw new ArgumentNullException(nameof(model)); var result = await ProcessConsent(model); if (result.HasValidationError) return View("Error"); return View("Success"); } private async Task ProcessConsent(DeviceAuthorizationInputModel model) { var result = new ProcessConsentResult(); var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.UserCode, model); } return result; } private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(userCode); if (request != null) { return CreateConsentViewModel(userCode, model, request); } return null; } private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { UserCode = userCode, Description = model?.Description, RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? DisplayName = apiScope.DisplayName ?? apiScope.Name, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [Authorize] public class DiagnosticsController : Controller { public async Task Index() { var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) { return NotFound(); } var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); return View(model); } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DiagnosticsViewModel { public DiagnosticsViewModel(AuthenticateResult result) { AuthenticateResult = result; if (result.Properties.Items.ContainsKey("client_list")) { var encoded = result.Properties.Items["client_list"]; var bytes = Base64Url.Decode(encoded); var value = Encoding.UTF8.GetString(bytes); Clients = JsonSerializer.Deserialize(value); } } public AuthenticateResult AuthenticateResult { get; } public IEnumerable Clients { get; } = new List(); } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public static class Extensions { /// /// Checks if the redirect URI is for a native client. /// /// public static bool IsNativeClient(this AuthorizationRequest context) { return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); } public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) { controller.HttpContext.Response.StatusCode = 200; controller.HttpContext.Response.Headers["Location"] = ""; return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Grants/GrantsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller allows a user to revoke grants given to clients /// [SecurityHeaders] [Authorize] public class GrantsController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; private readonly IResourceStore _resources; private readonly IEventService _events; public GrantsController(IIdentityServerInteractionService interaction, IClientStore clients, IResourceStore resources, IEventService events) { _interaction = interaction; _clients = clients; _resources = resources; _events = events; } /// /// Show list of grants /// [HttpGet] public async Task Index() { return View("Index", await BuildViewModelAsync()); } /// /// Handle postback to revoke a client /// [HttpPost] [ValidateAntiForgeryToken] public async Task Revoke(string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return RedirectToAction("Index"); } private async Task BuildViewModelAsync() { var grants = await _interaction.GetAllUserGrantsAsync(); var list = new List(); foreach(var grant in grants) { var client = await _clients.FindClientByIdAsync(grant.ClientId); if (client != null) { var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); var item = new GrantViewModel() { ClientId = client.ClientId, ClientName = client.ClientName ?? client.ClientId, ClientLogoUrl = client.LogoUri, ClientUrl = client.ClientUri, Description = grant.Description, Created = grant.CreationTime, Expires = grant.Expiration, IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() }; list.Add(item); } } return new GrantsViewModel { Grants = list }; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Grants/GrantsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class GrantsViewModel { public IEnumerable Grants { get; set; } } public class GrantViewModel { public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public string Description { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Home/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ErrorViewModel { public ErrorViewModel() { } public ErrorViewModel(string error) { Error = new ErrorMessage { Error = error }; } public ErrorMessage Error { get; set; } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/Home/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) { _interaction = interaction; _environment = environment; _logger = logger; } public IActionResult Index() { if (_environment.IsDevelopment()) { // only show in development return View(); } _logger.LogInformation("Homepage is disabled in production. Returning 404."); return NotFound(); } /// /// Shows the error page /// public async Task Error(string errorId) { var vm = new ErrorViewModel(); // retrieve error details from identityserver var message = await _interaction.GetErrorContextAsync(errorId); if (message != null) { vm.Error = message; if (!_environment.IsDevelopment()) { // only show in development message.ErrorDescription = null; } } return View("Error", vm); } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/SecurityHeadersAttribute.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class SecurityHeadersAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { var result = context.Result; if (result is ViewResult) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) { context.HttpContext.Response.Headers.Append("X-Content-Type-Options", "nosniff"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) { context.HttpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; // also consider adding upgrade-insecure-requests once you have HTTPS in place for production //csp += "upgrade-insecure-requests;"; // also an example if you need client images to be displayed from twitter // csp += "img-src 'self' https://pbs.twimg.com;"; // once for standards compliant browsers if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("Content-Security-Policy", csp); } // and once again for IE if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("X-Content-Security-Policy", csp); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy var referrer_policy = "no-referrer"; if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) { context.HttpContext.Response.Headers.Append("Referrer-Policy", referrer_policy); } } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Quickstart/TestUsers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class TestUsers { static readonly object UserAddress = new { street_address = "One Hacker Way", locality = "Heidelberg", postal_code = 69118, country = "Germany" }; public static List Users => new List { new() { SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new (Name, "Alice Smith"), new (GivenName, "Alice"), new (FamilyName, "Smith"), new (Email, "AliceSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://alice.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } }, new() { SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new (Name, "Bob Smith"), new (GivenName, "Bob"), new (FamilyName, "Smith"), new (Email, "BobSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://bob.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } } }; } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8; using IdentityServerHost.Quickstart.UI; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; namespace IdentityServer { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); var builder = services.AddIdentityServer() .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users); builder.AddDeveloperSigningCredential(); services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = ""; options.ClientSecret = ""; }) .AddOpenIdConnect("oidc", "Demo IdentityServer", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.SaveTokens = true; options.Authority = "https://demo.identityserver8.io/"; options.ClientId = "interactive.confidential"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Account/AccessDenied.cshtml ================================================ 

    Access Denied

    You do not have access to that resource.

    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Account/LoggedOut.cshtml ================================================ @model LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user ViewData["signed-out"] = true; }

    Logout You are now logged out

    @if (Model.PostLogoutRedirectUri != null) {
    Click here to return to the @Model.ClientName application.
    } @if (Model.SignOutIframeUrl != null) { }
    @section scripts { @if (Model.AutomaticRedirectAfterSignOut) { } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Account/Login.cshtml ================================================ @model LoginViewModel ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Account/Logout.cshtml ================================================ @model LogoutViewModel

    Logout

    Would you like to logout of IdentityServer?

    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Device/Success.cshtml ================================================

    Success

    You have successfully authorized the device

    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Device/UserCodeCapture.cshtml ================================================ @model string

    User Code

    Please enter the code displayed on your device.

    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
    @if (Model.ClientLogoUrl != null) { }

    @Model.ClientName is requesting your permission

    @if (Model.ConfirmUserCode) {

    Please confirm that the authorization request quotes the code: @Model.UserCode.

    }

    Uncheck the permissions you do not wish to grant.

    @if (Model.IdentityScopes.Any()) {
    Personal Information
      @foreach (var scope in Model.IdentityScopes) { }
    } @if (Model.ApiScopes.Any()) {
    Application Access
      @foreach (var scope in Model.ApiScopes) { }
    }
    Description
    @if (Model.AllowRememberConsent) {
    }
    @if (Model.ClientUrl != null) { @Model.ClientName }
    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

    Authentication Cookie

    Claims

    @foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in Model.AuthenticateResult.Properties.Items) {
    @prop.Key
    @prop.Value
    } @if (Model.Clients.Any()) {
    Clients
    @{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
    }
    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

    Client Application Permissions

    Below is the list of applications you have given permission to and the resources they have access to.

    @if (Model.Grants.Any() == false) {
    You have not given access to any applications
    } else { foreach (var grant in Model.Grants) {
    @if (grant.ClientLogoUrl != null) { } @grant.ClientName
      @if (grant.Description != null) {
    • @grant.Description
    • }
    • @grant.Created.ToString("yyyy-MM-dd")
    • @if (grant.Expires.HasValue) {
    • @grant.Expires.Value.ToString("yyyy-MM-dd")
    • } @if (grant.IdentityGrantNames.Any()) {
      • @foreach (var name in grant.IdentityGrantNames) {
      • @name
      • }
    • } @if (grant.ApiGrantNames.Any()) {
      • @foreach (var name in grant.ApiGrantNames) {
      • @name
      • }
    • }
    } }
    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @{ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); }

    Welcome to IdentityServer8 (version @version)

    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

    Error

    Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
    @errorDescription
    } }
    @if (request_id != null) {
    Request Id: @request_id
    }
    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

    You are now being returned to the application

    Once complete, you may close this tab.

    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
    @RenderBody()
    @RenderSection("scripts", required: false) ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/wwwroot/js/signin-redirect.js ================================================ // window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/IdentityServer/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/JavaScriptClient.csproj ================================================ ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ var app = WebApplication.Create(args); app.UseDefaultFiles(); app.UseStaticFiles(); await app.RunAsync(); ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:5003", "sslPort": 0 } }, "profiles": { "JavaScriptClient": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5003" } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.Builder; namespace JavaScriptClient { public class Startup { public void Configure(IApplicationBuilder app) { app.UseDefaultFiles(); app.UseStaticFiles(); } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/appsettings.json ================================================ { "Logging": { "LogLevel": { "Default": "Warning" } }, "AllowedHosts": "*" } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/wwwroot/app.js ================================================ /// function log() { document.getElementById('results').innerText = ''; Array.prototype.forEach.call(arguments, function (msg) { if (msg instanceof Error) { msg = "Error: " + msg.message; } else if (typeof msg !== 'string') { msg = JSON.stringify(msg, null, 2); } document.getElementById('results').innerText += msg + '\r\n'; }); } document.getElementById("login").addEventListener("click", login, false); document.getElementById("api").addEventListener("click", api, false); document.getElementById("logout").addEventListener("click", logout, false); var config = { authority: "https://localhost:5001", client_id: "js", redirect_uri: "https://localhost:5003/callback.html", response_type: "code", scope:"openid profile api1", post_logout_redirect_uri : "https://localhost:5003/index.html", }; var mgr = new Oidc.UserManager(config); mgr.getUser().then(function (user) { if (user) { log("User logged in", user.profile); } else { log("User not logged in"); } }); function login() { mgr.signinRedirect(); } function api() { mgr.getUser().then(function (user) { var url = "https://localhost:6001/identity"; var xhr = new XMLHttpRequest(); xhr.open("GET", url); xhr.onload = function () { log(xhr.status, JSON.parse(xhr.responseText)); } xhr.setRequestHeader("Authorization", "Bearer " + user.access_token); xhr.send(); }); } function logout() { mgr.signoutRedirect(); } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/wwwroot/callback.html ================================================ ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/wwwroot/index.html ================================================
    
    
        
        
    
    
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/JavaScriptClient/wwwroot/oidc-client.js
    ================================================
    var Oidc =
    /******/ (function(modules) { // webpackBootstrap
    /******/ 	// The module cache
    /******/ 	var installedModules = {};
    /******/
    /******/ 	// The require function
    /******/ 	function __webpack_require__(moduleId) {
    /******/
    /******/ 		// Check if module is in cache
    /******/ 		if(installedModules[moduleId]) {
    /******/ 			return installedModules[moduleId].exports;
    /******/ 		}
    /******/ 		// Create a new module (and put it into the cache)
    /******/ 		var module = installedModules[moduleId] = {
    /******/ 			i: moduleId,
    /******/ 			l: false,
    /******/ 			exports: {}
    /******/ 		};
    /******/
    /******/ 		// Execute the module function
    /******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
    /******/
    /******/ 		// Flag the module as loaded
    /******/ 		module.l = true;
    /******/
    /******/ 		// Return the exports of the module
    /******/ 		return module.exports;
    /******/ 	}
    /******/
    /******/
    /******/ 	// expose the modules object (__webpack_modules__)
    /******/ 	__webpack_require__.m = modules;
    /******/
    /******/ 	// expose the module cache
    /******/ 	__webpack_require__.c = installedModules;
    /******/
    /******/ 	// define getter function for harmony exports
    /******/ 	__webpack_require__.d = function(exports, name, getter) {
    /******/ 		if(!__webpack_require__.o(exports, name)) {
    /******/ 			Object.defineProperty(exports, name, { enumerable: true, get: getter });
    /******/ 		}
    /******/ 	};
    /******/
    /******/ 	// define __esModule on exports
    /******/ 	__webpack_require__.r = function(exports) {
    /******/ 		if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
    /******/ 			Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
    /******/ 		}
    /******/ 		Object.defineProperty(exports, '__esModule', { value: true });
    /******/ 	};
    /******/
    /******/ 	// create a fake namespace object
    /******/ 	// mode & 1: value is a module id, require it
    /******/ 	// mode & 2: merge all properties of value into the ns
    /******/ 	// mode & 4: return value when already ns object
    /******/ 	// mode & 8|1: behave like require
    /******/ 	__webpack_require__.t = function(value, mode) {
    /******/ 		if(mode & 1) value = __webpack_require__(value);
    /******/ 		if(mode & 8) return value;
    /******/ 		if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
    /******/ 		var ns = Object.create(null);
    /******/ 		__webpack_require__.r(ns);
    /******/ 		Object.defineProperty(ns, 'default', { enumerable: true, value: value });
    /******/ 		if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
    /******/ 		return ns;
    /******/ 	};
    /******/
    /******/ 	// getDefaultExport function for compatibility with non-harmony modules
    /******/ 	__webpack_require__.n = function(module) {
    /******/ 		var getter = module && module.__esModule ?
    /******/ 			function getDefault() { return module['default']; } :
    /******/ 			function getModuleExports() { return module; };
    /******/ 		__webpack_require__.d(getter, 'a', getter);
    /******/ 		return getter;
    /******/ 	};
    /******/
    /******/ 	// Object.prototype.hasOwnProperty.call
    /******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
    /******/
    /******/ 	// __webpack_public_path__
    /******/ 	__webpack_require__.p = "";
    /******/
    /******/
    /******/ 	// Load entry module and return exports
    /******/ 	return __webpack_require__(__webpack_require__.s = 0);
    /******/ })
    /************************************************************************/
    /******/ ({
    
    /***/ "./index.js":
    /*!******************!*\
      !*** ./index.js ***!
      \******************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    
    var _Log = __webpack_require__(/*! ./src/Log.js */ "./src/Log.js");
    
    var _OidcClient = __webpack_require__(/*! ./src/OidcClient.js */ "./src/OidcClient.js");
    
    var _OidcClientSettings = __webpack_require__(/*! ./src/OidcClientSettings.js */ "./src/OidcClientSettings.js");
    
    var _WebStorageStateStore = __webpack_require__(/*! ./src/WebStorageStateStore.js */ "./src/WebStorageStateStore.js");
    
    var _InMemoryWebStorage = __webpack_require__(/*! ./src/InMemoryWebStorage.js */ "./src/InMemoryWebStorage.js");
    
    var _UserManager = __webpack_require__(/*! ./src/UserManager.js */ "./src/UserManager.js");
    
    var _AccessTokenEvents = __webpack_require__(/*! ./src/AccessTokenEvents.js */ "./src/AccessTokenEvents.js");
    
    var _MetadataService = __webpack_require__(/*! ./src/MetadataService.js */ "./src/MetadataService.js");
    
    var _CordovaPopupNavigator = __webpack_require__(/*! ./src/CordovaPopupNavigator.js */ "./src/CordovaPopupNavigator.js");
    
    var _CordovaIFrameNavigator = __webpack_require__(/*! ./src/CordovaIFrameNavigator.js */ "./src/CordovaIFrameNavigator.js");
    
    var _CheckSessionIFrame = __webpack_require__(/*! ./src/CheckSessionIFrame.js */ "./src/CheckSessionIFrame.js");
    
    var _TokenRevocationClient = __webpack_require__(/*! ./src/TokenRevocationClient.js */ "./src/TokenRevocationClient.js");
    
    var _SessionMonitor = __webpack_require__(/*! ./src/SessionMonitor.js */ "./src/SessionMonitor.js");
    
    var _Global = __webpack_require__(/*! ./src/Global.js */ "./src/Global.js");
    
    var _User = __webpack_require__(/*! ./src/User.js */ "./src/User.js");
    
    var _version = __webpack_require__(/*! ./version.js */ "./version.js");
    
    // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    exports.default = {
        Version: _version.Version,
        Log: _Log.Log,
        OidcClient: _OidcClient.OidcClient,
        OidcClientSettings: _OidcClientSettings.OidcClientSettings,
        WebStorageStateStore: _WebStorageStateStore.WebStorageStateStore,
        InMemoryWebStorage: _InMemoryWebStorage.InMemoryWebStorage,
        UserManager: _UserManager.UserManager,
        AccessTokenEvents: _AccessTokenEvents.AccessTokenEvents,
        MetadataService: _MetadataService.MetadataService,
        CordovaPopupNavigator: _CordovaPopupNavigator.CordovaPopupNavigator,
        CordovaIFrameNavigator: _CordovaIFrameNavigator.CordovaIFrameNavigator,
        CheckSessionIFrame: _CheckSessionIFrame.CheckSessionIFrame,
        TokenRevocationClient: _TokenRevocationClient.TokenRevocationClient,
        SessionMonitor: _SessionMonitor.SessionMonitor,
        Global: _Global.Global,
        User: _User.User
    };
    module.exports = exports['default'];
    
    /***/ }),
    
    /***/ "./jsrsasign/dist/jsrsasign.js":
    /*!*************************************!*\
      !*** ./jsrsasign/dist/jsrsasign.js ***!
      \*************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    /* WEBPACK VAR INJECTION */(function(Buffer) {
    
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    
    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
    
    /*
     * jsrsasign(all) 8.0.12 (2018-04-22) (c) 2010-2018 Kenji Urushima | kjur.github.com/jsrsasign/license
     */
    
    var navigator = {};
    navigator.userAgent = false;
    
    var window = {};
    
    /*!
    Copyright (c) 2011, Yahoo! Inc. All rights reserved.
    Code licensed under the BSD License:
    http://developer.yahoo.com/yui/license.html
    version: 2.9.0
    */
    if (YAHOO === undefined) {
      var YAHOO = {};
    }YAHOO.lang = { extend: function extend(g, h, f) {
        if (!h || !g) {
          throw new Error("YAHOO.lang.extend failed, please check that all dependencies are included.");
        }var d = function d() {};d.prototype = h.prototype;g.prototype = new d();g.prototype.constructor = g;g.superclass = h.prototype;if (h.prototype.constructor == Object.prototype.constructor) {
          h.prototype.constructor = h;
        }if (f) {
          var b;for (b in f) {
            g.prototype[b] = f[b];
          }var e = function e() {},
              c = ["toString", "valueOf"];try {
            if (/MSIE/.test(navigator.userAgent)) {
              e = function e(j, i) {
                for (b = 0; b < c.length; b = b + 1) {
                  var l = c[b],
                      k = i[l];if (typeof k === "function" && k != Object.prototype[l]) {
                    j[l] = k;
                  }
                }
              };
            }
          } catch (a) {}e(g.prototype, f);
        }
      } };
    /*! CryptoJS v3.1.2 core-fix.js
     * code.google.com/p/crypto-js
     * (c) 2009-2013 by Jeff Mott. All rights reserved.
     * code.google.com/p/crypto-js/wiki/License
     * THIS IS FIX of 'core.js' to fix Hmac issue.
     * https://code.google.com/p/crypto-js/issues/detail?id=84
     * https://crypto-js.googlecode.com/svn-history/r667/branches/3.x/src/core.js
     */
    var CryptoJS = CryptoJS || function (e, g) {
      var a = {};var b = a.lib = {};var j = b.Base = function () {
        function n() {}return { extend: function extend(p) {
            n.prototype = this;var o = new n();if (p) {
              o.mixIn(p);
            }if (!o.hasOwnProperty("init")) {
              o.init = function () {
                o.$super.init.apply(this, arguments);
              };
            }o.init.prototype = o;o.$super = this;return o;
          }, create: function create() {
            var o = this.extend();o.init.apply(o, arguments);return o;
          }, init: function init() {}, mixIn: function mixIn(p) {
            for (var o in p) {
              if (p.hasOwnProperty(o)) {
                this[o] = p[o];
              }
            }if (p.hasOwnProperty("toString")) {
              this.toString = p.toString;
            }
          }, clone: function clone() {
            return this.init.prototype.extend(this);
          } };
      }();var l = b.WordArray = j.extend({ init: function init(o, n) {
          o = this.words = o || [];if (n != g) {
            this.sigBytes = n;
          } else {
            this.sigBytes = o.length * 4;
          }
        }, toString: function toString(n) {
          return (n || h).stringify(this);
        }, concat: function concat(t) {
          var q = this.words;var p = t.words;var n = this.sigBytes;var s = t.sigBytes;this.clamp();if (n % 4) {
            for (var r = 0; r < s; r++) {
              var o = p[r >>> 2] >>> 24 - r % 4 * 8 & 255;q[n + r >>> 2] |= o << 24 - (n + r) % 4 * 8;
            }
          } else {
            for (var r = 0; r < s; r += 4) {
              q[n + r >>> 2] = p[r >>> 2];
            }
          }this.sigBytes += s;return this;
        }, clamp: function clamp() {
          var o = this.words;var n = this.sigBytes;o[n >>> 2] &= 4294967295 << 32 - n % 4 * 8;o.length = e.ceil(n / 4);
        }, clone: function clone() {
          var n = j.clone.call(this);n.words = this.words.slice(0);return n;
        }, random: function random(p) {
          var o = [];for (var n = 0; n < p; n += 4) {
            o.push(e.random() * 4294967296 | 0);
          }return new l.init(o, p);
        } });var m = a.enc = {};var h = m.Hex = { stringify: function stringify(p) {
          var r = p.words;var o = p.sigBytes;var q = [];for (var n = 0; n < o; n++) {
            var s = r[n >>> 2] >>> 24 - n % 4 * 8 & 255;q.push((s >>> 4).toString(16));q.push((s & 15).toString(16));
          }return q.join("");
        }, parse: function parse(p) {
          var n = p.length;var q = [];for (var o = 0; o < n; o += 2) {
            q[o >>> 3] |= parseInt(p.substr(o, 2), 16) << 24 - o % 8 * 4;
          }return new l.init(q, n / 2);
        } };var d = m.Latin1 = { stringify: function stringify(q) {
          var r = q.words;var p = q.sigBytes;var n = [];for (var o = 0; o < p; o++) {
            var s = r[o >>> 2] >>> 24 - o % 4 * 8 & 255;n.push(String.fromCharCode(s));
          }return n.join("");
        }, parse: function parse(p) {
          var n = p.length;var q = [];for (var o = 0; o < n; o++) {
            q[o >>> 2] |= (p.charCodeAt(o) & 255) << 24 - o % 4 * 8;
          }return new l.init(q, n);
        } };var c = m.Utf8 = { stringify: function stringify(n) {
          try {
            return decodeURIComponent(escape(d.stringify(n)));
          } catch (o) {
            throw new Error("Malformed UTF-8 data");
          }
        }, parse: function parse(n) {
          return d.parse(unescape(encodeURIComponent(n)));
        } };var i = b.BufferedBlockAlgorithm = j.extend({ reset: function reset() {
          this._data = new l.init();this._nDataBytes = 0;
        }, _append: function _append(n) {
          if (typeof n == "string") {
            n = c.parse(n);
          }this._data.concat(n);this._nDataBytes += n.sigBytes;
        }, _process: function _process(w) {
          var q = this._data;var x = q.words;var n = q.sigBytes;var t = this.blockSize;var v = t * 4;var u = n / v;if (w) {
            u = e.ceil(u);
          } else {
            u = e.max((u | 0) - this._minBufferSize, 0);
          }var s = u * t;var r = e.min(s * 4, n);if (s) {
            for (var p = 0; p < s; p += t) {
              this._doProcessBlock(x, p);
            }var o = x.splice(0, s);q.sigBytes -= r;
          }return new l.init(o, r);
        }, clone: function clone() {
          var n = j.clone.call(this);n._data = this._data.clone();return n;
        }, _minBufferSize: 0 });var f = b.Hasher = i.extend({ cfg: j.extend(), init: function init(n) {
          this.cfg = this.cfg.extend(n);this.reset();
        }, reset: function reset() {
          i.reset.call(this);this._doReset();
        }, update: function update(n) {
          this._append(n);this._process();return this;
        }, finalize: function finalize(n) {
          if (n) {
            this._append(n);
          }var o = this._doFinalize();return o;
        }, blockSize: 512 / 32, _createHelper: function _createHelper(n) {
          return function (p, o) {
            return new n.init(o).finalize(p);
          };
        }, _createHmacHelper: function _createHmacHelper(n) {
          return function (p, o) {
            return new k.HMAC.init(n, o).finalize(p);
          };
        } });var k = a.algo = {};return a;
    }(Math);
    /*
    CryptoJS v3.1.2 x64-core-min.js
    code.google.com/p/crypto-js
    (c) 2009-2013 by Jeff Mott. All rights reserved.
    code.google.com/p/crypto-js/wiki/License
    */
    (function (g) {
      var a = CryptoJS,
          f = a.lib,
          e = f.Base,
          h = f.WordArray,
          a = a.x64 = {};a.Word = e.extend({ init: function init(b, c) {
          this.high = b;this.low = c;
        } });a.WordArray = e.extend({ init: function init(b, c) {
          b = this.words = b || [];this.sigBytes = c != g ? c : 8 * b.length;
        }, toX32: function toX32() {
          for (var b = this.words, c = b.length, a = [], d = 0; d < c; d++) {
            var e = b[d];a.push(e.high);a.push(e.low);
          }return h.create(a, this.sigBytes);
        }, clone: function clone() {
          for (var b = e.clone.call(this), c = b.words = this.words.slice(0), a = c.length, d = 0; d < a; d++) {
            c[d] = c[d].clone();
          }return b;
        } });
    })();
    
    /*
    CryptoJS v3.1.2 enc-base64.js
    code.google.com/p/crypto-js
    (c) 2009-2013 by Jeff Mott. All rights reserved.
    code.google.com/p/crypto-js/wiki/License
    */
    (function () {
      var h = CryptoJS,
          j = h.lib.WordArray;h.enc.Base64 = { stringify: function stringify(b) {
          var e = b.words,
              f = b.sigBytes,
              c = this._map;b.clamp();b = [];for (var a = 0; a < f; a += 3) {
            for (var d = (e[a >>> 2] >>> 24 - 8 * (a % 4) & 255) << 16 | (e[a + 1 >>> 2] >>> 24 - 8 * ((a + 1) % 4) & 255) << 8 | e[a + 2 >>> 2] >>> 24 - 8 * ((a + 2) % 4) & 255, g = 0; 4 > g && a + 0.75 * g < f; g++) {
              b.push(c.charAt(d >>> 6 * (3 - g) & 63));
            }
          }if (e = c.charAt(64)) for (; b.length % 4;) {
            b.push(e);
          }return b.join("");
        }, parse: function parse(b) {
          var e = b.length,
              f = this._map,
              c = f.charAt(64);c && (c = b.indexOf(c), -1 != c && (e = c));for (var c = [], a = 0, d = 0; d < e; d++) {
            if (d % 4) {
              var g = f.indexOf(b.charAt(d - 1)) << 2 * (d % 4),
                  h = f.indexOf(b.charAt(d)) >>> 6 - 2 * (d % 4);c[a >>> 2] |= (g | h) << 24 - 8 * (a % 4);a++;
            }
          }return j.create(c, a);
        }, _map: "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" };
    })();
    
    /*
    CryptoJS v3.1.2 sha256-min.js
    code.google.com/p/crypto-js
    (c) 2009-2013 by Jeff Mott. All rights reserved.
    code.google.com/p/crypto-js/wiki/License
    */
    (function (k) {
      for (var g = CryptoJS, h = g.lib, v = h.WordArray, j = h.Hasher, h = g.algo, s = [], t = [], u = function u(q) {
        return 4294967296 * (q - (q | 0)) | 0;
      }, l = 2, b = 0; 64 > b;) {
        var d;a: {
          d = l;for (var w = k.sqrt(d), r = 2; r <= w; r++) {
            if (!(d % r)) {
              d = !1;break a;
            }
          }d = !0;
        }d && (8 > b && (s[b] = u(k.pow(l, 0.5))), t[b] = u(k.pow(l, 1 / 3)), b++);l++;
      }var n = [],
          h = h.SHA256 = j.extend({ _doReset: function _doReset() {
          this._hash = new v.init(s.slice(0));
        }, _doProcessBlock: function _doProcessBlock(q, h) {
          for (var a = this._hash.words, c = a[0], d = a[1], b = a[2], k = a[3], f = a[4], g = a[5], j = a[6], l = a[7], e = 0; 64 > e; e++) {
            if (16 > e) n[e] = q[h + e] | 0;else {
              var m = n[e - 15],
                  p = n[e - 2];n[e] = ((m << 25 | m >>> 7) ^ (m << 14 | m >>> 18) ^ m >>> 3) + n[e - 7] + ((p << 15 | p >>> 17) ^ (p << 13 | p >>> 19) ^ p >>> 10) + n[e - 16];
            }m = l + ((f << 26 | f >>> 6) ^ (f << 21 | f >>> 11) ^ (f << 7 | f >>> 25)) + (f & g ^ ~f & j) + t[e] + n[e];p = ((c << 30 | c >>> 2) ^ (c << 19 | c >>> 13) ^ (c << 10 | c >>> 22)) + (c & d ^ c & b ^ d & b);l = j;j = g;g = f;f = k + m | 0;k = b;b = d;d = c;c = m + p | 0;
          }a[0] = a[0] + c | 0;a[1] = a[1] + d | 0;a[2] = a[2] + b | 0;a[3] = a[3] + k | 0;a[4] = a[4] + f | 0;a[5] = a[5] + g | 0;a[6] = a[6] + j | 0;a[7] = a[7] + l | 0;
        }, _doFinalize: function _doFinalize() {
          var d = this._data,
              b = d.words,
              a = 8 * this._nDataBytes,
              c = 8 * d.sigBytes;
          b[c >>> 5] |= 128 << 24 - c % 32;b[(c + 64 >>> 9 << 4) + 14] = k.floor(a / 4294967296);b[(c + 64 >>> 9 << 4) + 15] = a;d.sigBytes = 4 * b.length;this._process();return this._hash;
        }, clone: function clone() {
          var b = j.clone.call(this);b._hash = this._hash.clone();return b;
        } });g.SHA256 = j._createHelper(h);g.HmacSHA256 = j._createHmacHelper(h);
    })(Math);
    
    /*
    CryptoJS v3.1.2 sha512-min.js
    code.google.com/p/crypto-js
    (c) 2009-2013 by Jeff Mott. All rights reserved.
    code.google.com/p/crypto-js/wiki/License
    */
    (function () {
      function a() {
        return d.create.apply(d, arguments);
      }for (var n = CryptoJS, r = n.lib.Hasher, e = n.x64, d = e.Word, T = e.WordArray, e = n.algo, ea = [a(1116352408, 3609767458), a(1899447441, 602891725), a(3049323471, 3964484399), a(3921009573, 2173295548), a(961987163, 4081628472), a(1508970993, 3053834265), a(2453635748, 2937671579), a(2870763221, 3664609560), a(3624381080, 2734883394), a(310598401, 1164996542), a(607225278, 1323610764), a(1426881987, 3590304994), a(1925078388, 4068182383), a(2162078206, 991336113), a(2614888103, 633803317), a(3248222580, 3479774868), a(3835390401, 2666613458), a(4022224774, 944711139), a(264347078, 2341262773), a(604807628, 2007800933), a(770255983, 1495990901), a(1249150122, 1856431235), a(1555081692, 3175218132), a(1996064986, 2198950837), a(2554220882, 3999719339), a(2821834349, 766784016), a(2952996808, 2566594879), a(3210313671, 3203337956), a(3336571891, 1034457026), a(3584528711, 2466948901), a(113926993, 3758326383), a(338241895, 168717936), a(666307205, 1188179964), a(773529912, 1546045734), a(1294757372, 1522805485), a(1396182291, 2643833823), a(1695183700, 2343527390), a(1986661051, 1014477480), a(2177026350, 1206759142), a(2456956037, 344077627), a(2730485921, 1290863460), a(2820302411, 3158454273), a(3259730800, 3505952657), a(3345764771, 106217008), a(3516065817, 3606008344), a(3600352804, 1432725776), a(4094571909, 1467031594), a(275423344, 851169720), a(430227734, 3100823752), a(506948616, 1363258195), a(659060556, 3750685593), a(883997877, 3785050280), a(958139571, 3318307427), a(1322822218, 3812723403), a(1537002063, 2003034995), a(1747873779, 3602036899), a(1955562222, 1575990012), a(2024104815, 1125592928), a(2227730452, 2716904306), a(2361852424, 442776044), a(2428436474, 593698344), a(2756734187, 3733110249), a(3204031479, 2999351573), a(3329325298, 3815920427), a(3391569614, 3928383900), a(3515267271, 566280711), a(3940187606, 3454069534), a(4118630271, 4000239992), a(116418474, 1914138554), a(174292421, 2731055270), a(289380356, 3203993006), a(460393269, 320620315), a(685471733, 587496836), a(852142971, 1086792851), a(1017036298, 365543100), a(1126000580, 2618297676), a(1288033470, 3409855158), a(1501505948, 4234509866), a(1607167915, 987167468), a(1816402316, 1246189591)], v = [], w = 0; 80 > w; w++) {
        v[w] = a();
      }e = e.SHA512 = r.extend({ _doReset: function _doReset() {
          this._hash = new T.init([new d.init(1779033703, 4089235720), new d.init(3144134277, 2227873595), new d.init(1013904242, 4271175723), new d.init(2773480762, 1595750129), new d.init(1359893119, 2917565137), new d.init(2600822924, 725511199), new d.init(528734635, 4215389547), new d.init(1541459225, 327033209)]);
        }, _doProcessBlock: function _doProcessBlock(a, d) {
          for (var f = this._hash.words, F = f[0], e = f[1], n = f[2], r = f[3], G = f[4], H = f[5], I = f[6], f = f[7], w = F.high, J = F.low, X = e.high, K = e.low, Y = n.high, L = n.low, Z = r.high, M = r.low, $ = G.high, N = G.low, aa = H.high, O = H.low, ba = I.high, P = I.low, ca = f.high, Q = f.low, k = w, g = J, z = X, x = K, A = Y, y = L, U = Z, B = M, l = $, h = N, R = aa, C = O, S = ba, D = P, V = ca, E = Q, m = 0; 80 > m; m++) {
            var s = v[m];if (16 > m) var j = s.high = a[d + 2 * m] | 0,
                b = s.low = a[d + 2 * m + 1] | 0;else {
              var j = v[m - 15],
                  b = j.high,
                  p = j.low,
                  j = (b >>> 1 | p << 31) ^ (b >>> 8 | p << 24) ^ b >>> 7,
                  p = (p >>> 1 | b << 31) ^ (p >>> 8 | b << 24) ^ (p >>> 7 | b << 25),
                  u = v[m - 2],
                  b = u.high,
                  c = u.low,
                  u = (b >>> 19 | c << 13) ^ (b << 3 | c >>> 29) ^ b >>> 6,
                  c = (c >>> 19 | b << 13) ^ (c << 3 | b >>> 29) ^ (c >>> 6 | b << 26),
                  b = v[m - 7],
                  W = b.high,
                  t = v[m - 16],
                  q = t.high,
                  t = t.low,
                  b = p + b.low,
                  j = j + W + (b >>> 0 < p >>> 0 ? 1 : 0),
                  b = b + c,
                  j = j + u + (b >>> 0 < c >>> 0 ? 1 : 0),
                  b = b + t,
                  j = j + q + (b >>> 0 < t >>> 0 ? 1 : 0);s.high = j;s.low = b;
            }var W = l & R ^ ~l & S,
                t = h & C ^ ~h & D,
                s = k & z ^ k & A ^ z & A,
                T = g & x ^ g & y ^ x & y,
                p = (k >>> 28 | g << 4) ^ (k << 30 | g >>> 2) ^ (k << 25 | g >>> 7),
                u = (g >>> 28 | k << 4) ^ (g << 30 | k >>> 2) ^ (g << 25 | k >>> 7),
                c = ea[m],
                fa = c.high,
                da = c.low,
                c = E + ((h >>> 14 | l << 18) ^ (h >>> 18 | l << 14) ^ (h << 23 | l >>> 9)),
                q = V + ((l >>> 14 | h << 18) ^ (l >>> 18 | h << 14) ^ (l << 23 | h >>> 9)) + (c >>> 0 < E >>> 0 ? 1 : 0),
                c = c + t,
                q = q + W + (c >>> 0 < t >>> 0 ? 1 : 0),
                c = c + da,
                q = q + fa + (c >>> 0 < da >>> 0 ? 1 : 0),
                c = c + b,
                q = q + j + (c >>> 0 < b >>> 0 ? 1 : 0),
                b = u + T,
                s = p + s + (b >>> 0 < u >>> 0 ? 1 : 0),
                V = S,
                E = D,
                S = R,
                D = C,
                R = l,
                C = h,
                h = B + c | 0,
                l = U + q + (h >>> 0 < B >>> 0 ? 1 : 0) | 0,
                U = A,
                B = y,
                A = z,
                y = x,
                z = k,
                x = g,
                g = c + b | 0,
                k = q + s + (g >>> 0 < c >>> 0 ? 1 : 0) | 0;
          }J = F.low = J + g;F.high = w + k + (J >>> 0 < g >>> 0 ? 1 : 0);K = e.low = K + x;e.high = X + z + (K >>> 0 < x >>> 0 ? 1 : 0);L = n.low = L + y;n.high = Y + A + (L >>> 0 < y >>> 0 ? 1 : 0);M = r.low = M + B;r.high = Z + U + (M >>> 0 < B >>> 0 ? 1 : 0);N = G.low = N + h;G.high = $ + l + (N >>> 0 < h >>> 0 ? 1 : 0);O = H.low = O + C;H.high = aa + R + (O >>> 0 < C >>> 0 ? 1 : 0);P = I.low = P + D;
          I.high = ba + S + (P >>> 0 < D >>> 0 ? 1 : 0);Q = f.low = Q + E;f.high = ca + V + (Q >>> 0 < E >>> 0 ? 1 : 0);
        }, _doFinalize: function _doFinalize() {
          var a = this._data,
              d = a.words,
              f = 8 * this._nDataBytes,
              e = 8 * a.sigBytes;d[e >>> 5] |= 128 << 24 - e % 32;d[(e + 128 >>> 10 << 5) + 30] = Math.floor(f / 4294967296);d[(e + 128 >>> 10 << 5) + 31] = f;a.sigBytes = 4 * d.length;this._process();return this._hash.toX32();
        }, clone: function clone() {
          var a = r.clone.call(this);a._hash = this._hash.clone();return a;
        }, blockSize: 32 });n.SHA512 = r._createHelper(e);n.HmacSHA512 = r._createHmacHelper(e);
    })();
    
    /*
    CryptoJS v3.1.2 sha384-min.js
    code.google.com/p/crypto-js
    (c) 2009-2013 by Jeff Mott. All rights reserved.
    code.google.com/p/crypto-js/wiki/License
    */
    (function () {
      var c = CryptoJS,
          a = c.x64,
          b = a.Word,
          e = a.WordArray,
          a = c.algo,
          d = a.SHA512,
          a = a.SHA384 = d.extend({ _doReset: function _doReset() {
          this._hash = new e.init([new b.init(3418070365, 3238371032), new b.init(1654270250, 914150663), new b.init(2438529370, 812702999), new b.init(355462360, 4144912697), new b.init(1731405415, 4290775857), new b.init(2394180231, 1750603025), new b.init(3675008525, 1694076839), new b.init(1203062813, 3204075428)]);
        }, _doFinalize: function _doFinalize() {
          var a = d._doFinalize.call(this);a.sigBytes -= 16;return a;
        } });c.SHA384 = d._createHelper(a);c.HmacSHA384 = d._createHmacHelper(a);
    })();
    
    /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
     */
    var b64map = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";var b64pad = "=";function hex2b64(d) {
      var b;var e;var a = "";for (b = 0; b + 3 <= d.length; b += 3) {
        e = parseInt(d.substring(b, b + 3), 16);a += b64map.charAt(e >> 6) + b64map.charAt(e & 63);
      }if (b + 1 == d.length) {
        e = parseInt(d.substring(b, b + 1), 16);a += b64map.charAt(e << 2);
      } else {
        if (b + 2 == d.length) {
          e = parseInt(d.substring(b, b + 2), 16);a += b64map.charAt(e >> 2) + b64map.charAt((e & 3) << 4);
        }
      }if (b64pad) {
        while ((a.length & 3) > 0) {
          a += b64pad;
        }
      }return a;
    }function b64tohex(f) {
      var d = "";var e;var b = 0;var c;var a;for (e = 0; e < f.length; ++e) {
        if (f.charAt(e) == b64pad) {
          break;
        }a = b64map.indexOf(f.charAt(e));if (a < 0) {
          continue;
        }if (b == 0) {
          d += int2char(a >> 2);c = a & 3;b = 1;
        } else {
          if (b == 1) {
            d += int2char(c << 2 | a >> 4);c = a & 15;b = 2;
          } else {
            if (b == 2) {
              d += int2char(c);d += int2char(a >> 2);c = a & 3;b = 3;
            } else {
              d += int2char(c << 2 | a >> 4);d += int2char(a & 15);b = 0;
            }
          }
        }
      }if (b == 1) {
        d += int2char(c << 2);
      }return d;
    }function b64toBA(e) {
      var d = b64tohex(e);var c;var b = new Array();for (c = 0; 2 * c < d.length; ++c) {
        b[c] = parseInt(d.substring(2 * c, 2 * c + 2), 16);
      }return b;
    };
    /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
     */
    var dbits;var canary = 244837814094590;var j_lm = (canary & 16777215) == 15715070;function BigInteger(e, d, f) {
      if (e != null) {
        if ("number" == typeof e) {
          this.fromNumber(e, d, f);
        } else {
          if (d == null && "string" != typeof e) {
            this.fromString(e, 256);
          } else {
            this.fromString(e, d);
          }
        }
      }
    }function nbi() {
      return new BigInteger(null);
    }function am1(f, a, b, e, h, g) {
      while (--g >= 0) {
        var d = a * this[f++] + b[e] + h;h = Math.floor(d / 67108864);b[e++] = d & 67108863;
      }return h;
    }function am2(f, q, r, e, o, a) {
      var k = q & 32767,
          p = q >> 15;while (--a >= 0) {
        var d = this[f] & 32767;var g = this[f++] >> 15;var b = p * d + g * k;d = k * d + ((b & 32767) << 15) + r[e] + (o & 1073741823);o = (d >>> 30) + (b >>> 15) + p * g + (o >>> 30);r[e++] = d & 1073741823;
      }return o;
    }function am3(f, q, r, e, o, a) {
      var k = q & 16383,
          p = q >> 14;while (--a >= 0) {
        var d = this[f] & 16383;var g = this[f++] >> 14;var b = p * d + g * k;d = k * d + ((b & 16383) << 14) + r[e] + o;o = (d >> 28) + (b >> 14) + p * g;r[e++] = d & 268435455;
      }return o;
    }if (j_lm && navigator.appName == "Microsoft Internet Explorer") {
      BigInteger.prototype.am = am2;dbits = 30;
    } else {
      if (j_lm && navigator.appName != "Netscape") {
        BigInteger.prototype.am = am1;dbits = 26;
      } else {
        BigInteger.prototype.am = am3;dbits = 28;
      }
    }BigInteger.prototype.DB = dbits;BigInteger.prototype.DM = (1 << dbits) - 1;BigInteger.prototype.DV = 1 << dbits;var BI_FP = 52;BigInteger.prototype.FV = Math.pow(2, BI_FP);BigInteger.prototype.F1 = BI_FP - dbits;BigInteger.prototype.F2 = 2 * dbits - BI_FP;var BI_RM = "0123456789abcdefghijklmnopqrstuvwxyz";var BI_RC = new Array();var rr, vv;rr = "0".charCodeAt(0);for (vv = 0; vv <= 9; ++vv) {
      BI_RC[rr++] = vv;
    }rr = "a".charCodeAt(0);for (vv = 10; vv < 36; ++vv) {
      BI_RC[rr++] = vv;
    }rr = "A".charCodeAt(0);for (vv = 10; vv < 36; ++vv) {
      BI_RC[rr++] = vv;
    }function int2char(a) {
      return BI_RM.charAt(a);
    }function intAt(b, a) {
      var d = BI_RC[b.charCodeAt(a)];return d == null ? -1 : d;
    }function bnpCopyTo(b) {
      for (var a = this.t - 1; a >= 0; --a) {
        b[a] = this[a];
      }b.t = this.t;b.s = this.s;
    }function bnpFromInt(a) {
      this.t = 1;this.s = a < 0 ? -1 : 0;if (a > 0) {
        this[0] = a;
      } else {
        if (a < -1) {
          this[0] = a + this.DV;
        } else {
          this.t = 0;
        }
      }
    }function nbv(a) {
      var b = nbi();b.fromInt(a);return b;
    }function bnpFromString(h, c) {
      var e;if (c == 16) {
        e = 4;
      } else {
        if (c == 8) {
          e = 3;
        } else {
          if (c == 256) {
            e = 8;
          } else {
            if (c == 2) {
              e = 1;
            } else {
              if (c == 32) {
                e = 5;
              } else {
                if (c == 4) {
                  e = 2;
                } else {
                  this.fromRadix(h, c);return;
                }
              }
            }
          }
        }
      }this.t = 0;this.s = 0;var g = h.length,
          d = false,
          f = 0;while (--g >= 0) {
        var a = e == 8 ? h[g] & 255 : intAt(h, g);if (a < 0) {
          if (h.charAt(g) == "-") {
            d = true;
          }continue;
        }d = false;if (f == 0) {
          this[this.t++] = a;
        } else {
          if (f + e > this.DB) {
            this[this.t - 1] |= (a & (1 << this.DB - f) - 1) << f;this[this.t++] = a >> this.DB - f;
          } else {
            this[this.t - 1] |= a << f;
          }
        }f += e;if (f >= this.DB) {
          f -= this.DB;
        }
      }if (e == 8 && (h[0] & 128) != 0) {
        this.s = -1;if (f > 0) {
          this[this.t - 1] |= (1 << this.DB - f) - 1 << f;
        }
      }this.clamp();if (d) {
        BigInteger.ZERO.subTo(this, this);
      }
    }function bnpClamp() {
      var a = this.s & this.DM;while (this.t > 0 && this[this.t - 1] == a) {
        --this.t;
      }
    }function bnToString(c) {
      if (this.s < 0) {
        return "-" + this.negate().toString(c);
      }var e;if (c == 16) {
        e = 4;
      } else {
        if (c == 8) {
          e = 3;
        } else {
          if (c == 2) {
            e = 1;
          } else {
            if (c == 32) {
              e = 5;
            } else {
              if (c == 4) {
                e = 2;
              } else {
                return this.toRadix(c);
              }
            }
          }
        }
      }var g = (1 << e) - 1,
          l,
          a = false,
          h = "",
          f = this.t;var j = this.DB - f * this.DB % e;if (f-- > 0) {
        if (j < this.DB && (l = this[f] >> j) > 0) {
          a = true;h = int2char(l);
        }while (f >= 0) {
          if (j < e) {
            l = (this[f] & (1 << j) - 1) << e - j;l |= this[--f] >> (j += this.DB - e);
          } else {
            l = this[f] >> (j -= e) & g;if (j <= 0) {
              j += this.DB;--f;
            }
          }if (l > 0) {
            a = true;
          }if (a) {
            h += int2char(l);
          }
        }
      }return a ? h : "0";
    }function bnNegate() {
      var a = nbi();BigInteger.ZERO.subTo(this, a);return a;
    }function bnAbs() {
      return this.s < 0 ? this.negate() : this;
    }function bnCompareTo(b) {
      var d = this.s - b.s;if (d != 0) {
        return d;
      }var c = this.t;d = c - b.t;if (d != 0) {
        return this.s < 0 ? -d : d;
      }while (--c >= 0) {
        if ((d = this[c] - b[c]) != 0) {
          return d;
        }
      }return 0;
    }function nbits(a) {
      var c = 1,
          b;if ((b = a >>> 16) != 0) {
        a = b;c += 16;
      }if ((b = a >> 8) != 0) {
        a = b;c += 8;
      }if ((b = a >> 4) != 0) {
        a = b;c += 4;
      }if ((b = a >> 2) != 0) {
        a = b;c += 2;
      }if ((b = a >> 1) != 0) {
        a = b;c += 1;
      }return c;
    }function bnBitLength() {
      if (this.t <= 0) {
        return 0;
      }return this.DB * (this.t - 1) + nbits(this[this.t - 1] ^ this.s & this.DM);
    }function bnpDLShiftTo(c, b) {
      var a;for (a = this.t - 1; a >= 0; --a) {
        b[a + c] = this[a];
      }for (a = c - 1; a >= 0; --a) {
        b[a] = 0;
      }b.t = this.t + c;b.s = this.s;
    }function bnpDRShiftTo(c, b) {
      for (var a = c; a < this.t; ++a) {
        b[a - c] = this[a];
      }b.t = Math.max(this.t - c, 0);b.s = this.s;
    }function bnpLShiftTo(j, e) {
      var b = j % this.DB;var a = this.DB - b;var g = (1 << a) - 1;var f = Math.floor(j / this.DB),
          h = this.s << b & this.DM,
          d;for (d = this.t - 1; d >= 0; --d) {
        e[d + f + 1] = this[d] >> a | h;h = (this[d] & g) << b;
      }for (d = f - 1; d >= 0; --d) {
        e[d] = 0;
      }e[f] = h;e.t = this.t + f + 1;e.s = this.s;e.clamp();
    }function bnpRShiftTo(g, d) {
      d.s = this.s;var e = Math.floor(g / this.DB);if (e >= this.t) {
        d.t = 0;return;
      }var b = g % this.DB;var a = this.DB - b;var f = (1 << b) - 1;d[0] = this[e] >> b;for (var c = e + 1; c < this.t; ++c) {
        d[c - e - 1] |= (this[c] & f) << a;d[c - e] = this[c] >> b;
      }if (b > 0) {
        d[this.t - e - 1] |= (this.s & f) << a;
      }d.t = this.t - e;d.clamp();
    }function bnpSubTo(d, f) {
      var e = 0,
          g = 0,
          b = Math.min(d.t, this.t);while (e < b) {
        g += this[e] - d[e];f[e++] = g & this.DM;g >>= this.DB;
      }if (d.t < this.t) {
        g -= d.s;while (e < this.t) {
          g += this[e];f[e++] = g & this.DM;g >>= this.DB;
        }g += this.s;
      } else {
        g += this.s;while (e < d.t) {
          g -= d[e];f[e++] = g & this.DM;g >>= this.DB;
        }g -= d.s;
      }f.s = g < 0 ? -1 : 0;if (g < -1) {
        f[e++] = this.DV + g;
      } else {
        if (g > 0) {
          f[e++] = g;
        }
      }f.t = e;f.clamp();
    }function bnpMultiplyTo(c, e) {
      var b = this.abs(),
          f = c.abs();var d = b.t;e.t = d + f.t;while (--d >= 0) {
        e[d] = 0;
      }for (d = 0; d < f.t; ++d) {
        e[d + b.t] = b.am(0, f[d], e, d, 0, b.t);
      }e.s = 0;e.clamp();if (this.s != c.s) {
        BigInteger.ZERO.subTo(e, e);
      }
    }function bnpSquareTo(d) {
      var a = this.abs();var b = d.t = 2 * a.t;while (--b >= 0) {
        d[b] = 0;
      }for (b = 0; b < a.t - 1; ++b) {
        var e = a.am(b, a[b], d, 2 * b, 0, 1);if ((d[b + a.t] += a.am(b + 1, 2 * a[b], d, 2 * b + 1, e, a.t - b - 1)) >= a.DV) {
          d[b + a.t] -= a.DV;d[b + a.t + 1] = 1;
        }
      }if (d.t > 0) {
        d[d.t - 1] += a.am(b, a[b], d, 2 * b, 0, 1);
      }d.s = 0;d.clamp();
    }function bnpDivRemTo(n, h, g) {
      var w = n.abs();if (w.t <= 0) {
        return;
      }var k = this.abs();if (k.t < w.t) {
        if (h != null) {
          h.fromInt(0);
        }if (g != null) {
          this.copyTo(g);
        }return;
      }if (g == null) {
        g = nbi();
      }var d = nbi(),
          a = this.s,
          l = n.s;var v = this.DB - nbits(w[w.t - 1]);if (v > 0) {
        w.lShiftTo(v, d);k.lShiftTo(v, g);
      } else {
        w.copyTo(d);k.copyTo(g);
      }var p = d.t;var b = d[p - 1];if (b == 0) {
        return;
      }var o = b * (1 << this.F1) + (p > 1 ? d[p - 2] >> this.F2 : 0);var A = this.FV / o,
          z = (1 << this.F1) / o,
          x = 1 << this.F2;var u = g.t,
          s = u - p,
          f = h == null ? nbi() : h;d.dlShiftTo(s, f);if (g.compareTo(f) >= 0) {
        g[g.t++] = 1;g.subTo(f, g);
      }BigInteger.ONE.dlShiftTo(p, f);f.subTo(d, d);while (d.t < p) {
        d[d.t++] = 0;
      }while (--s >= 0) {
        var c = g[--u] == b ? this.DM : Math.floor(g[u] * A + (g[u - 1] + x) * z);if ((g[u] += d.am(0, c, g, s, 0, p)) < c) {
          d.dlShiftTo(s, f);g.subTo(f, g);while (g[u] < --c) {
            g.subTo(f, g);
          }
        }
      }if (h != null) {
        g.drShiftTo(p, h);if (a != l) {
          BigInteger.ZERO.subTo(h, h);
        }
      }g.t = p;g.clamp();if (v > 0) {
        g.rShiftTo(v, g);
      }if (a < 0) {
        BigInteger.ZERO.subTo(g, g);
      }
    }function bnMod(b) {
      var c = nbi();this.abs().divRemTo(b, null, c);if (this.s < 0 && c.compareTo(BigInteger.ZERO) > 0) {
        b.subTo(c, c);
      }return c;
    }function Classic(a) {
      this.m = a;
    }function cConvert(a) {
      if (a.s < 0 || a.compareTo(this.m) >= 0) {
        return a.mod(this.m);
      } else {
        return a;
      }
    }function cRevert(a) {
      return a;
    }function cReduce(a) {
      a.divRemTo(this.m, null, a);
    }function cMulTo(a, c, b) {
      a.multiplyTo(c, b);this.reduce(b);
    }function cSqrTo(a, b) {
      a.squareTo(b);this.reduce(b);
    }Classic.prototype.convert = cConvert;Classic.prototype.revert = cRevert;Classic.prototype.reduce = cReduce;Classic.prototype.mulTo = cMulTo;Classic.prototype.sqrTo = cSqrTo;function bnpInvDigit() {
      if (this.t < 1) {
        return 0;
      }var a = this[0];if ((a & 1) == 0) {
        return 0;
      }var b = a & 3;b = b * (2 - (a & 15) * b) & 15;b = b * (2 - (a & 255) * b) & 255;b = b * (2 - ((a & 65535) * b & 65535)) & 65535;b = b * (2 - a * b % this.DV) % this.DV;return b > 0 ? this.DV - b : -b;
    }function Montgomery(a) {
      this.m = a;this.mp = a.invDigit();this.mpl = this.mp & 32767;this.mph = this.mp >> 15;this.um = (1 << a.DB - 15) - 1;this.mt2 = 2 * a.t;
    }function montConvert(a) {
      var b = nbi();a.abs().dlShiftTo(this.m.t, b);b.divRemTo(this.m, null, b);if (a.s < 0 && b.compareTo(BigInteger.ZERO) > 0) {
        this.m.subTo(b, b);
      }return b;
    }function montRevert(a) {
      var b = nbi();a.copyTo(b);this.reduce(b);return b;
    }function montReduce(a) {
      while (a.t <= this.mt2) {
        a[a.t++] = 0;
      }for (var c = 0; c < this.m.t; ++c) {
        var b = a[c] & 32767;var d = b * this.mpl + ((b * this.mph + (a[c] >> 15) * this.mpl & this.um) << 15) & a.DM;b = c + this.m.t;a[b] += this.m.am(0, d, a, c, 0, this.m.t);while (a[b] >= a.DV) {
          a[b] -= a.DV;a[++b]++;
        }
      }a.clamp();a.drShiftTo(this.m.t, a);if (a.compareTo(this.m) >= 0) {
        a.subTo(this.m, a);
      }
    }function montSqrTo(a, b) {
      a.squareTo(b);this.reduce(b);
    }function montMulTo(a, c, b) {
      a.multiplyTo(c, b);this.reduce(b);
    }Montgomery.prototype.convert = montConvert;Montgomery.prototype.revert = montRevert;Montgomery.prototype.reduce = montReduce;Montgomery.prototype.mulTo = montMulTo;Montgomery.prototype.sqrTo = montSqrTo;function bnpIsEven() {
      return (this.t > 0 ? this[0] & 1 : this.s) == 0;
    }function bnpExp(h, j) {
      if (h > 4294967295 || h < 1) {
        return BigInteger.ONE;
      }var f = nbi(),
          a = nbi(),
          d = j.convert(this),
          c = nbits(h) - 1;d.copyTo(f);while (--c >= 0) {
        j.sqrTo(f, a);if ((h & 1 << c) > 0) {
          j.mulTo(a, d, f);
        } else {
          var b = f;f = a;a = b;
        }
      }return j.revert(f);
    }function bnModPowInt(b, a) {
      var c;if (b < 256 || a.isEven()) {
        c = new Classic(a);
      } else {
        c = new Montgomery(a);
      }return this.exp(b, c);
    }BigInteger.prototype.copyTo = bnpCopyTo;BigInteger.prototype.fromInt = bnpFromInt;BigInteger.prototype.fromString = bnpFromString;BigInteger.prototype.clamp = bnpClamp;BigInteger.prototype.dlShiftTo = bnpDLShiftTo;BigInteger.prototype.drShiftTo = bnpDRShiftTo;BigInteger.prototype.lShiftTo = bnpLShiftTo;BigInteger.prototype.rShiftTo = bnpRShiftTo;BigInteger.prototype.subTo = bnpSubTo;BigInteger.prototype.multiplyTo = bnpMultiplyTo;BigInteger.prototype.squareTo = bnpSquareTo;BigInteger.prototype.divRemTo = bnpDivRemTo;BigInteger.prototype.invDigit = bnpInvDigit;BigInteger.prototype.isEven = bnpIsEven;BigInteger.prototype.exp = bnpExp;BigInteger.prototype.toString = bnToString;BigInteger.prototype.negate = bnNegate;BigInteger.prototype.abs = bnAbs;BigInteger.prototype.compareTo = bnCompareTo;BigInteger.prototype.bitLength = bnBitLength;BigInteger.prototype.mod = bnMod;BigInteger.prototype.modPowInt = bnModPowInt;BigInteger.ZERO = nbv(0);BigInteger.ONE = nbv(1);
    /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
     */
    function bnClone() {
      var a = nbi();this.copyTo(a);return a;
    }function bnIntValue() {
      if (this.s < 0) {
        if (this.t == 1) {
          return this[0] - this.DV;
        } else {
          if (this.t == 0) {
            return -1;
          }
        }
      } else {
        if (this.t == 1) {
          return this[0];
        } else {
          if (this.t == 0) {
            return 0;
          }
        }
      }return (this[1] & (1 << 32 - this.DB) - 1) << this.DB | this[0];
    }function bnByteValue() {
      return this.t == 0 ? this.s : this[0] << 24 >> 24;
    }function bnShortValue() {
      return this.t == 0 ? this.s : this[0] << 16 >> 16;
    }function bnpChunkSize(a) {
      return Math.floor(Math.LN2 * this.DB / Math.log(a));
    }function bnSigNum() {
      if (this.s < 0) {
        return -1;
      } else {
        if (this.t <= 0 || this.t == 1 && this[0] <= 0) {
          return 0;
        } else {
          return 1;
        }
      }
    }function bnpToRadix(c) {
      if (c == null) {
        c = 10;
      }if (this.signum() == 0 || c < 2 || c > 36) {
        return "0";
      }var f = this.chunkSize(c);var e = Math.pow(c, f);var i = nbv(e),
          j = nbi(),
          h = nbi(),
          g = "";this.divRemTo(i, j, h);while (j.signum() > 0) {
        g = (e + h.intValue()).toString(c).substr(1) + g;j.divRemTo(i, j, h);
      }return h.intValue().toString(c) + g;
    }function bnpFromRadix(m, h) {
      this.fromInt(0);if (h == null) {
        h = 10;
      }var f = this.chunkSize(h);var g = Math.pow(h, f),
          e = false,
          a = 0,
          l = 0;for (var c = 0; c < m.length; ++c) {
        var k = intAt(m, c);if (k < 0) {
          if (m.charAt(c) == "-" && this.signum() == 0) {
            e = true;
          }continue;
        }l = h * l + k;if (++a >= f) {
          this.dMultiply(g);this.dAddOffset(l, 0);a = 0;l = 0;
        }
      }if (a > 0) {
        this.dMultiply(Math.pow(h, a));this.dAddOffset(l, 0);
      }if (e) {
        BigInteger.ZERO.subTo(this, this);
      }
    }function bnpFromNumber(f, e, h) {
      if ("number" == typeof e) {
        if (f < 2) {
          this.fromInt(1);
        } else {
          this.fromNumber(f, h);if (!this.testBit(f - 1)) {
            this.bitwiseTo(BigInteger.ONE.shiftLeft(f - 1), op_or, this);
          }if (this.isEven()) {
            this.dAddOffset(1, 0);
          }while (!this.isProbablePrime(e)) {
            this.dAddOffset(2, 0);if (this.bitLength() > f) {
              this.subTo(BigInteger.ONE.shiftLeft(f - 1), this);
            }
          }
        }
      } else {
        var d = new Array(),
            g = f & 7;d.length = (f >> 3) + 1;e.nextBytes(d);if (g > 0) {
          d[0] &= (1 << g) - 1;
        } else {
          d[0] = 0;
        }this.fromString(d, 256);
      }
    }function bnToByteArray() {
      var b = this.t,
          c = new Array();c[0] = this.s;var e = this.DB - b * this.DB % 8,
          f,
          a = 0;if (b-- > 0) {
        if (e < this.DB && (f = this[b] >> e) != (this.s & this.DM) >> e) {
          c[a++] = f | this.s << this.DB - e;
        }while (b >= 0) {
          if (e < 8) {
            f = (this[b] & (1 << e) - 1) << 8 - e;f |= this[--b] >> (e += this.DB - 8);
          } else {
            f = this[b] >> (e -= 8) & 255;if (e <= 0) {
              e += this.DB;--b;
            }
          }if ((f & 128) != 0) {
            f |= -256;
          }if (a == 0 && (this.s & 128) != (f & 128)) {
            ++a;
          }if (a > 0 || f != this.s) {
            c[a++] = f;
          }
        }
      }return c;
    }function bnEquals(b) {
      return this.compareTo(b) == 0;
    }function bnMin(b) {
      return this.compareTo(b) < 0 ? this : b;
    }function bnMax(b) {
      return this.compareTo(b) > 0 ? this : b;
    }function bnpBitwiseTo(c, h, e) {
      var d,
          g,
          b = Math.min(c.t, this.t);for (d = 0; d < b; ++d) {
        e[d] = h(this[d], c[d]);
      }if (c.t < this.t) {
        g = c.s & this.DM;for (d = b; d < this.t; ++d) {
          e[d] = h(this[d], g);
        }e.t = this.t;
      } else {
        g = this.s & this.DM;for (d = b; d < c.t; ++d) {
          e[d] = h(g, c[d]);
        }e.t = c.t;
      }e.s = h(this.s, c.s);e.clamp();
    }function op_and(a, b) {
      return a & b;
    }function bnAnd(b) {
      var c = nbi();this.bitwiseTo(b, op_and, c);return c;
    }function op_or(a, b) {
      return a | b;
    }function bnOr(b) {
      var c = nbi();this.bitwiseTo(b, op_or, c);return c;
    }function op_xor(a, b) {
      return a ^ b;
    }function bnXor(b) {
      var c = nbi();this.bitwiseTo(b, op_xor, c);return c;
    }function op_andnot(a, b) {
      return a & ~b;
    }function bnAndNot(b) {
      var c = nbi();this.bitwiseTo(b, op_andnot, c);return c;
    }function bnNot() {
      var b = nbi();for (var a = 0; a < this.t; ++a) {
        b[a] = this.DM & ~this[a];
      }b.t = this.t;b.s = ~this.s;return b;
    }function bnShiftLeft(b) {
      var a = nbi();if (b < 0) {
        this.rShiftTo(-b, a);
      } else {
        this.lShiftTo(b, a);
      }return a;
    }function bnShiftRight(b) {
      var a = nbi();if (b < 0) {
        this.lShiftTo(-b, a);
      } else {
        this.rShiftTo(b, a);
      }return a;
    }function lbit(a) {
      if (a == 0) {
        return -1;
      }var b = 0;if ((a & 65535) == 0) {
        a >>= 16;b += 16;
      }if ((a & 255) == 0) {
        a >>= 8;b += 8;
      }if ((a & 15) == 0) {
        a >>= 4;b += 4;
      }if ((a & 3) == 0) {
        a >>= 2;b += 2;
      }if ((a & 1) == 0) {
        ++b;
      }return b;
    }function bnGetLowestSetBit() {
      for (var a = 0; a < this.t; ++a) {
        if (this[a] != 0) {
          return a * this.DB + lbit(this[a]);
        }
      }if (this.s < 0) {
        return this.t * this.DB;
      }return -1;
    }function cbit(a) {
      var b = 0;while (a != 0) {
        a &= a - 1;++b;
      }return b;
    }function bnBitCount() {
      var c = 0,
          a = this.s & this.DM;for (var b = 0; b < this.t; ++b) {
        c += cbit(this[b] ^ a);
      }return c;
    }function bnTestBit(b) {
      var a = Math.floor(b / this.DB);if (a >= this.t) {
        return this.s != 0;
      }return (this[a] & 1 << b % this.DB) != 0;
    }function bnpChangeBit(c, b) {
      var a = BigInteger.ONE.shiftLeft(c);this.bitwiseTo(a, b, a);return a;
    }function bnSetBit(a) {
      return this.changeBit(a, op_or);
    }function bnClearBit(a) {
      return this.changeBit(a, op_andnot);
    }function bnFlipBit(a) {
      return this.changeBit(a, op_xor);
    }function bnpAddTo(d, f) {
      var e = 0,
          g = 0,
          b = Math.min(d.t, this.t);while (e < b) {
        g += this[e] + d[e];f[e++] = g & this.DM;g >>= this.DB;
      }if (d.t < this.t) {
        g += d.s;while (e < this.t) {
          g += this[e];f[e++] = g & this.DM;g >>= this.DB;
        }g += this.s;
      } else {
        g += this.s;while (e < d.t) {
          g += d[e];f[e++] = g & this.DM;g >>= this.DB;
        }g += d.s;
      }f.s = g < 0 ? -1 : 0;if (g > 0) {
        f[e++] = g;
      } else {
        if (g < -1) {
          f[e++] = this.DV + g;
        }
      }f.t = e;f.clamp();
    }function bnAdd(b) {
      var c = nbi();this.addTo(b, c);return c;
    }function bnSubtract(b) {
      var c = nbi();this.subTo(b, c);return c;
    }function bnMultiply(b) {
      var c = nbi();this.multiplyTo(b, c);return c;
    }function bnSquare() {
      var a = nbi();this.squareTo(a);return a;
    }function bnDivide(b) {
      var c = nbi();this.divRemTo(b, c, null);return c;
    }function bnRemainder(b) {
      var c = nbi();this.divRemTo(b, null, c);return c;
    }function bnDivideAndRemainder(b) {
      var d = nbi(),
          c = nbi();this.divRemTo(b, d, c);return new Array(d, c);
    }function bnpDMultiply(a) {
      this[this.t] = this.am(0, a - 1, this, 0, 0, this.t);++this.t;this.clamp();
    }function bnpDAddOffset(b, a) {
      if (b == 0) {
        return;
      }while (this.t <= a) {
        this[this.t++] = 0;
      }this[a] += b;while (this[a] >= this.DV) {
        this[a] -= this.DV;if (++a >= this.t) {
          this[this.t++] = 0;
        }++this[a];
      }
    }function NullExp() {}function nNop(a) {
      return a;
    }function nMulTo(a, c, b) {
      a.multiplyTo(c, b);
    }function nSqrTo(a, b) {
      a.squareTo(b);
    }NullExp.prototype.convert = nNop;NullExp.prototype.revert = nNop;NullExp.prototype.mulTo = nMulTo;NullExp.prototype.sqrTo = nSqrTo;function bnPow(a) {
      return this.exp(a, new NullExp());
    }function bnpMultiplyLowerTo(b, f, e) {
      var d = Math.min(this.t + b.t, f);e.s = 0;e.t = d;while (d > 0) {
        e[--d] = 0;
      }var c;for (c = e.t - this.t; d < c; ++d) {
        e[d + this.t] = this.am(0, b[d], e, d, 0, this.t);
      }for (c = Math.min(b.t, f); d < c; ++d) {
        this.am(0, b[d], e, d, 0, f - d);
      }e.clamp();
    }function bnpMultiplyUpperTo(b, e, d) {
      --e;var c = d.t = this.t + b.t - e;d.s = 0;while (--c >= 0) {
        d[c] = 0;
      }for (c = Math.max(e - this.t, 0); c < b.t; ++c) {
        d[this.t + c - e] = this.am(e - c, b[c], d, 0, 0, this.t + c - e);
      }d.clamp();d.drShiftTo(1, d);
    }function Barrett(a) {
      this.r2 = nbi();this.q3 = nbi();BigInteger.ONE.dlShiftTo(2 * a.t, this.r2);this.mu = this.r2.divide(a);this.m = a;
    }function barrettConvert(a) {
      if (a.s < 0 || a.t > 2 * this.m.t) {
        return a.mod(this.m);
      } else {
        if (a.compareTo(this.m) < 0) {
          return a;
        } else {
          var b = nbi();a.copyTo(b);this.reduce(b);return b;
        }
      }
    }function barrettRevert(a) {
      return a;
    }function barrettReduce(a) {
      a.drShiftTo(this.m.t - 1, this.r2);if (a.t > this.m.t + 1) {
        a.t = this.m.t + 1;a.clamp();
      }this.mu.multiplyUpperTo(this.r2, this.m.t + 1, this.q3);this.m.multiplyLowerTo(this.q3, this.m.t + 1, this.r2);while (a.compareTo(this.r2) < 0) {
        a.dAddOffset(1, this.m.t + 1);
      }a.subTo(this.r2, a);while (a.compareTo(this.m) >= 0) {
        a.subTo(this.m, a);
      }
    }function barrettSqrTo(a, b) {
      a.squareTo(b);this.reduce(b);
    }function barrettMulTo(a, c, b) {
      a.multiplyTo(c, b);this.reduce(b);
    }Barrett.prototype.convert = barrettConvert;Barrett.prototype.revert = barrettRevert;Barrett.prototype.reduce = barrettReduce;Barrett.prototype.mulTo = barrettMulTo;Barrett.prototype.sqrTo = barrettSqrTo;function bnModPow(q, f) {
      var o = q.bitLength(),
          h,
          b = nbv(1),
          v;if (o <= 0) {
        return b;
      } else {
        if (o < 18) {
          h = 1;
        } else {
          if (o < 48) {
            h = 3;
          } else {
            if (o < 144) {
              h = 4;
            } else {
              if (o < 768) {
                h = 5;
              } else {
                h = 6;
              }
            }
          }
        }
      }if (o < 8) {
        v = new Classic(f);
      } else {
        if (f.isEven()) {
          v = new Barrett(f);
        } else {
          v = new Montgomery(f);
        }
      }var p = new Array(),
          d = 3,
          s = h - 1,
          a = (1 << h) - 1;p[1] = v.convert(this);if (h > 1) {
        var A = nbi();v.sqrTo(p[1], A);while (d <= a) {
          p[d] = nbi();v.mulTo(A, p[d - 2], p[d]);d += 2;
        }
      }var l = q.t - 1,
          x,
          u = true,
          c = nbi(),
          y;o = nbits(q[l]) - 1;while (l >= 0) {
        if (o >= s) {
          x = q[l] >> o - s & a;
        } else {
          x = (q[l] & (1 << o + 1) - 1) << s - o;if (l > 0) {
            x |= q[l - 1] >> this.DB + o - s;
          }
        }d = h;while ((x & 1) == 0) {
          x >>= 1;--d;
        }if ((o -= d) < 0) {
          o += this.DB;--l;
        }if (u) {
          p[x].copyTo(b);u = false;
        } else {
          while (d > 1) {
            v.sqrTo(b, c);v.sqrTo(c, b);d -= 2;
          }if (d > 0) {
            v.sqrTo(b, c);
          } else {
            y = b;b = c;c = y;
          }v.mulTo(c, p[x], b);
        }while (l >= 0 && (q[l] & 1 << o) == 0) {
          v.sqrTo(b, c);y = b;b = c;c = y;if (--o < 0) {
            o = this.DB - 1;--l;
          }
        }
      }return v.revert(b);
    }function bnGCD(c) {
      var b = this.s < 0 ? this.negate() : this.clone();var h = c.s < 0 ? c.negate() : c.clone();if (b.compareTo(h) < 0) {
        var e = b;b = h;h = e;
      }var d = b.getLowestSetBit(),
          f = h.getLowestSetBit();if (f < 0) {
        return b;
      }if (d < f) {
        f = d;
      }if (f > 0) {
        b.rShiftTo(f, b);h.rShiftTo(f, h);
      }while (b.signum() > 0) {
        if ((d = b.getLowestSetBit()) > 0) {
          b.rShiftTo(d, b);
        }if ((d = h.getLowestSetBit()) > 0) {
          h.rShiftTo(d, h);
        }if (b.compareTo(h) >= 0) {
          b.subTo(h, b);b.rShiftTo(1, b);
        } else {
          h.subTo(b, h);h.rShiftTo(1, h);
        }
      }if (f > 0) {
        h.lShiftTo(f, h);
      }return h;
    }function bnpModInt(e) {
      if (e <= 0) {
        return 0;
      }var c = this.DV % e,
          b = this.s < 0 ? e - 1 : 0;if (this.t > 0) {
        if (c == 0) {
          b = this[0] % e;
        } else {
          for (var a = this.t - 1; a >= 0; --a) {
            b = (c * b + this[a]) % e;
          }
        }
      }return b;
    }function bnModInverse(f) {
      var j = f.isEven();if (this.isEven() && j || f.signum() == 0) {
        return BigInteger.ZERO;
      }var i = f.clone(),
          h = this.clone();var g = nbv(1),
          e = nbv(0),
          l = nbv(0),
          k = nbv(1);while (i.signum() != 0) {
        while (i.isEven()) {
          i.rShiftTo(1, i);if (j) {
            if (!g.isEven() || !e.isEven()) {
              g.addTo(this, g);e.subTo(f, e);
            }g.rShiftTo(1, g);
          } else {
            if (!e.isEven()) {
              e.subTo(f, e);
            }
          }e.rShiftTo(1, e);
        }while (h.isEven()) {
          h.rShiftTo(1, h);if (j) {
            if (!l.isEven() || !k.isEven()) {
              l.addTo(this, l);k.subTo(f, k);
            }l.rShiftTo(1, l);
          } else {
            if (!k.isEven()) {
              k.subTo(f, k);
            }
          }k.rShiftTo(1, k);
        }if (i.compareTo(h) >= 0) {
          i.subTo(h, i);if (j) {
            g.subTo(l, g);
          }e.subTo(k, e);
        } else {
          h.subTo(i, h);if (j) {
            l.subTo(g, l);
          }k.subTo(e, k);
        }
      }if (h.compareTo(BigInteger.ONE) != 0) {
        return BigInteger.ZERO;
      }if (k.compareTo(f) >= 0) {
        return k.subtract(f);
      }if (k.signum() < 0) {
        k.addTo(f, k);
      } else {
        return k;
      }if (k.signum() < 0) {
        return k.add(f);
      } else {
        return k;
      }
    }var lowprimes = [2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173, 179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281, 283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409, 419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541, 547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659, 661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809, 811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941, 947, 953, 967, 971, 977, 983, 991, 997];var lplim = (1 << 26) / lowprimes[lowprimes.length - 1];function bnIsProbablePrime(e) {
      var d,
          b = this.abs();if (b.t == 1 && b[0] <= lowprimes[lowprimes.length - 1]) {
        for (d = 0; d < lowprimes.length; ++d) {
          if (b[0] == lowprimes[d]) {
            return true;
          }
        }return false;
      }if (b.isEven()) {
        return false;
      }d = 1;while (d < lowprimes.length) {
        var a = lowprimes[d],
            c = d + 1;while (c < lowprimes.length && a < lplim) {
          a *= lowprimes[c++];
        }a = b.modInt(a);while (d < c) {
          if (a % lowprimes[d++] == 0) {
            return false;
          }
        }
      }return b.millerRabin(e);
    }function bnpMillerRabin(f) {
      var g = this.subtract(BigInteger.ONE);var c = g.getLowestSetBit();if (c <= 0) {
        return false;
      }var h = g.shiftRight(c);f = f + 1 >> 1;if (f > lowprimes.length) {
        f = lowprimes.length;
      }var b = nbi();for (var e = 0; e < f; ++e) {
        b.fromInt(lowprimes[Math.floor(Math.random() * lowprimes.length)]);var l = b.modPow(h, this);if (l.compareTo(BigInteger.ONE) != 0 && l.compareTo(g) != 0) {
          var d = 1;while (d++ < c && l.compareTo(g) != 0) {
            l = l.modPowInt(2, this);if (l.compareTo(BigInteger.ONE) == 0) {
              return false;
            }
          }if (l.compareTo(g) != 0) {
            return false;
          }
        }
      }return true;
    }BigInteger.prototype.chunkSize = bnpChunkSize;BigInteger.prototype.toRadix = bnpToRadix;BigInteger.prototype.fromRadix = bnpFromRadix;BigInteger.prototype.fromNumber = bnpFromNumber;BigInteger.prototype.bitwiseTo = bnpBitwiseTo;BigInteger.prototype.changeBit = bnpChangeBit;BigInteger.prototype.addTo = bnpAddTo;BigInteger.prototype.dMultiply = bnpDMultiply;BigInteger.prototype.dAddOffset = bnpDAddOffset;BigInteger.prototype.multiplyLowerTo = bnpMultiplyLowerTo;BigInteger.prototype.multiplyUpperTo = bnpMultiplyUpperTo;BigInteger.prototype.modInt = bnpModInt;BigInteger.prototype.millerRabin = bnpMillerRabin;BigInteger.prototype.clone = bnClone;BigInteger.prototype.intValue = bnIntValue;BigInteger.prototype.byteValue = bnByteValue;BigInteger.prototype.shortValue = bnShortValue;BigInteger.prototype.signum = bnSigNum;BigInteger.prototype.toByteArray = bnToByteArray;BigInteger.prototype.equals = bnEquals;BigInteger.prototype.min = bnMin;BigInteger.prototype.max = bnMax;BigInteger.prototype.and = bnAnd;BigInteger.prototype.or = bnOr;BigInteger.prototype.xor = bnXor;BigInteger.prototype.andNot = bnAndNot;BigInteger.prototype.not = bnNot;BigInteger.prototype.shiftLeft = bnShiftLeft;BigInteger.prototype.shiftRight = bnShiftRight;BigInteger.prototype.getLowestSetBit = bnGetLowestSetBit;BigInteger.prototype.bitCount = bnBitCount;BigInteger.prototype.testBit = bnTestBit;BigInteger.prototype.setBit = bnSetBit;BigInteger.prototype.clearBit = bnClearBit;BigInteger.prototype.flipBit = bnFlipBit;BigInteger.prototype.add = bnAdd;BigInteger.prototype.subtract = bnSubtract;BigInteger.prototype.multiply = bnMultiply;BigInteger.prototype.divide = bnDivide;BigInteger.prototype.remainder = bnRemainder;BigInteger.prototype.divideAndRemainder = bnDivideAndRemainder;BigInteger.prototype.modPow = bnModPow;BigInteger.prototype.modInverse = bnModInverse;BigInteger.prototype.pow = bnPow;BigInteger.prototype.gcd = bnGCD;BigInteger.prototype.isProbablePrime = bnIsProbablePrime;BigInteger.prototype.square = bnSquare;
    /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
     */
    function Arcfour() {
      this.i = 0;this.j = 0;this.S = new Array();
    }function ARC4init(d) {
      var c, a, b;for (c = 0; c < 256; ++c) {
        this.S[c] = c;
      }a = 0;for (c = 0; c < 256; ++c) {
        a = a + this.S[c] + d[c % d.length] & 255;b = this.S[c];this.S[c] = this.S[a];this.S[a] = b;
      }this.i = 0;this.j = 0;
    }function ARC4next() {
      var a;this.i = this.i + 1 & 255;this.j = this.j + this.S[this.i] & 255;a = this.S[this.i];this.S[this.i] = this.S[this.j];this.S[this.j] = a;return this.S[a + this.S[this.i] & 255];
    }Arcfour.prototype.init = ARC4init;Arcfour.prototype.next = ARC4next;function prng_newstate() {
      return new Arcfour();
    }var rng_psize = 256;
    /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
     */
    var rng_state;var rng_pool;var rng_pptr;function rng_seed_int(a) {
      rng_pool[rng_pptr++] ^= a & 255;rng_pool[rng_pptr++] ^= a >> 8 & 255;rng_pool[rng_pptr++] ^= a >> 16 & 255;rng_pool[rng_pptr++] ^= a >> 24 & 255;if (rng_pptr >= rng_psize) {
        rng_pptr -= rng_psize;
      }
    }function rng_seed_time() {
      rng_seed_int(new Date().getTime());
    }if (rng_pool == null) {
      rng_pool = new Array();rng_pptr = 0;var t;if (window !== undefined && (window.crypto !== undefined || window.msCrypto !== undefined)) {
        var crypto = window.crypto || window.msCrypto;if (crypto.getRandomValues) {
          var ua = new Uint8Array(32);crypto.getRandomValues(ua);for (t = 0; t < 32; ++t) {
            rng_pool[rng_pptr++] = ua[t];
          }
        } else {
          if (navigator.appName == "Netscape" && navigator.appVersion < "5") {
            var z = window.crypto.random(32);for (t = 0; t < z.length; ++t) {
              rng_pool[rng_pptr++] = z.charCodeAt(t) & 255;
            }
          }
        }
      }while (rng_pptr < rng_psize) {
        t = Math.floor(65536 * Math.random());rng_pool[rng_pptr++] = t >>> 8;rng_pool[rng_pptr++] = t & 255;
      }rng_pptr = 0;rng_seed_time();
    }function rng_get_byte() {
      if (rng_state == null) {
        rng_seed_time();rng_state = prng_newstate();rng_state.init(rng_pool);for (rng_pptr = 0; rng_pptr < rng_pool.length; ++rng_pptr) {
          rng_pool[rng_pptr] = 0;
        }rng_pptr = 0;
      }return rng_state.next();
    }function rng_get_bytes(b) {
      var a;for (a = 0; a < b.length; ++a) {
        b[a] = rng_get_byte();
      }
    }function SecureRandom() {}SecureRandom.prototype.nextBytes = rng_get_bytes;
    /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
     */
    function parseBigInt(b, a) {
      return new BigInteger(b, a);
    }function linebrk(c, d) {
      var a = "";var b = 0;while (b + d < c.length) {
        a += c.substring(b, b + d) + "\n";b += d;
      }return a + c.substring(b, c.length);
    }function byte2Hex(a) {
      if (a < 16) {
        return "0" + a.toString(16);
      } else {
        return a.toString(16);
      }
    }function pkcs1pad2(e, h) {
      if (h < e.length + 11) {
        throw "Message too long for RSA";return null;
      }var g = new Array();var d = e.length - 1;while (d >= 0 && h > 0) {
        var f = e.charCodeAt(d--);if (f < 128) {
          g[--h] = f;
        } else {
          if (f > 127 && f < 2048) {
            g[--h] = f & 63 | 128;g[--h] = f >> 6 | 192;
          } else {
            g[--h] = f & 63 | 128;g[--h] = f >> 6 & 63 | 128;g[--h] = f >> 12 | 224;
          }
        }
      }g[--h] = 0;var b = new SecureRandom();var a = new Array();while (h > 2) {
        a[0] = 0;while (a[0] == 0) {
          b.nextBytes(a);
        }g[--h] = a[0];
      }g[--h] = 2;g[--h] = 0;return new BigInteger(g);
    }function oaep_mgf1_arr(c, a, e) {
      var b = "",
          d = 0;while (b.length < a) {
        b += e(String.fromCharCode.apply(String, c.concat([(d & 4278190080) >> 24, (d & 16711680) >> 16, (d & 65280) >> 8, d & 255])));d += 1;
      }return b;
    }function oaep_pad(q, a, f, l) {
      var c = KJUR.crypto.MessageDigest;var o = KJUR.crypto.Util;var b = null;if (!f) {
        f = "sha1";
      }if (typeof f === "string") {
        b = c.getCanonicalAlgName(f);l = c.getHashLength(b);f = function f(i) {
          return hextorstr(o.hashHex(rstrtohex(i), b));
        };
      }if (q.length + 2 * l + 2 > a) {
        throw "Message too long for RSA";
      }var k = "",
          e;for (e = 0; e < a - q.length - 2 * l - 2; e += 1) {
        k += "\x00";
      }var h = f("") + k + "\x01" + q;var g = new Array(l);new SecureRandom().nextBytes(g);var j = oaep_mgf1_arr(g, h.length, f);var p = [];for (e = 0; e < h.length; e += 1) {
        p[e] = h.charCodeAt(e) ^ j.charCodeAt(e);
      }var m = oaep_mgf1_arr(p, g.length, f);var d = [0];for (e = 0; e < g.length; e += 1) {
        d[e + 1] = g[e] ^ m.charCodeAt(e);
      }return new BigInteger(d.concat(p));
    }function RSAKey() {
      this.n = null;this.e = 0;this.d = null;this.p = null;this.q = null;this.dmp1 = null;this.dmq1 = null;this.coeff = null;
    }function RSASetPublic(b, a) {
      this.isPublic = true;this.isPrivate = false;if (typeof b !== "string") {
        this.n = b;this.e = a;
      } else {
        if (b != null && a != null && b.length > 0 && a.length > 0) {
          this.n = parseBigInt(b, 16);this.e = parseInt(a, 16);
        } else {
          throw "Invalid RSA public key";
        }
      }
    }function RSADoPublic(a) {
      return a.modPowInt(this.e, this.n);
    }function RSAEncrypt(d) {
      var a = pkcs1pad2(d, this.n.bitLength() + 7 >> 3);if (a == null) {
        return null;
      }var e = this.doPublic(a);if (e == null) {
        return null;
      }var b = e.toString(16);if ((b.length & 1) == 0) {
        return b;
      } else {
        return "0" + b;
      }
    }function RSAEncryptOAEP(f, e, b) {
      var a = oaep_pad(f, this.n.bitLength() + 7 >> 3, e, b);if (a == null) {
        return null;
      }var g = this.doPublic(a);if (g == null) {
        return null;
      }var d = g.toString(16);if ((d.length & 1) == 0) {
        return d;
      } else {
        return "0" + d;
      }
    }RSAKey.prototype.doPublic = RSADoPublic;RSAKey.prototype.setPublic = RSASetPublic;RSAKey.prototype.encrypt = RSAEncrypt;RSAKey.prototype.encryptOAEP = RSAEncryptOAEP;RSAKey.prototype.type = "RSA";
    /*! (c) Tom Wu | http://www-cs-students.stanford.edu/~tjw/jsbn/
     */
    function ECFieldElementFp(b, a) {
      this.x = a;this.q = b;
    }function feFpEquals(a) {
      if (a == this) {
        return true;
      }return this.q.equals(a.q) && this.x.equals(a.x);
    }function feFpToBigInteger() {
      return this.x;
    }function feFpNegate() {
      return new ECFieldElementFp(this.q, this.x.negate().mod(this.q));
    }function feFpAdd(a) {
      return new ECFieldElementFp(this.q, this.x.add(a.toBigInteger()).mod(this.q));
    }function feFpSubtract(a) {
      return new ECFieldElementFp(this.q, this.x.subtract(a.toBigInteger()).mod(this.q));
    }function feFpMultiply(a) {
      return new ECFieldElementFp(this.q, this.x.multiply(a.toBigInteger()).mod(this.q));
    }function feFpSquare() {
      return new ECFieldElementFp(this.q, this.x.square().mod(this.q));
    }function feFpDivide(a) {
      return new ECFieldElementFp(this.q, this.x.multiply(a.toBigInteger().modInverse(this.q)).mod(this.q));
    }ECFieldElementFp.prototype.equals = feFpEquals;ECFieldElementFp.prototype.toBigInteger = feFpToBigInteger;ECFieldElementFp.prototype.negate = feFpNegate;ECFieldElementFp.prototype.add = feFpAdd;ECFieldElementFp.prototype.subtract = feFpSubtract;ECFieldElementFp.prototype.multiply = feFpMultiply;ECFieldElementFp.prototype.square = feFpSquare;ECFieldElementFp.prototype.divide = feFpDivide;function ECPointFp(c, a, d, b) {
      this.curve = c;this.x = a;this.y = d;if (b == null) {
        this.z = BigInteger.ONE;
      } else {
        this.z = b;
      }this.zinv = null;
    }function pointFpGetX() {
      if (this.zinv == null) {
        this.zinv = this.z.modInverse(this.curve.q);
      }return this.curve.fromBigInteger(this.x.toBigInteger().multiply(this.zinv).mod(this.curve.q));
    }function pointFpGetY() {
      if (this.zinv == null) {
        this.zinv = this.z.modInverse(this.curve.q);
      }return this.curve.fromBigInteger(this.y.toBigInteger().multiply(this.zinv).mod(this.curve.q));
    }function pointFpEquals(a) {
      if (a == this) {
        return true;
      }if (this.isInfinity()) {
        return a.isInfinity();
      }if (a.isInfinity()) {
        return this.isInfinity();
      }var c, b;c = a.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(a.z)).mod(this.curve.q);if (!c.equals(BigInteger.ZERO)) {
        return false;
      }b = a.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(a.z)).mod(this.curve.q);return b.equals(BigInteger.ZERO);
    }function pointFpIsInfinity() {
      if (this.x == null && this.y == null) {
        return true;
      }return this.z.equals(BigInteger.ZERO) && !this.y.toBigInteger().equals(BigInteger.ZERO);
    }function pointFpNegate() {
      return new ECPointFp(this.curve, this.x, this.y.negate(), this.z);
    }function pointFpAdd(l) {
      if (this.isInfinity()) {
        return l;
      }if (l.isInfinity()) {
        return this;
      }var p = l.y.toBigInteger().multiply(this.z).subtract(this.y.toBigInteger().multiply(l.z)).mod(this.curve.q);var o = l.x.toBigInteger().multiply(this.z).subtract(this.x.toBigInteger().multiply(l.z)).mod(this.curve.q);if (BigInteger.ZERO.equals(o)) {
        if (BigInteger.ZERO.equals(p)) {
          return this.twice();
        }return this.curve.getInfinity();
      }var j = new BigInteger("3");var e = this.x.toBigInteger();var n = this.y.toBigInteger();var c = l.x.toBigInteger();var k = l.y.toBigInteger();var m = o.square();var i = m.multiply(o);var d = e.multiply(m);var g = p.square().multiply(this.z);var a = g.subtract(d.shiftLeft(1)).multiply(l.z).subtract(i).multiply(o).mod(this.curve.q);var h = d.multiply(j).multiply(p).subtract(n.multiply(i)).subtract(g.multiply(p)).multiply(l.z).add(p.multiply(i)).mod(this.curve.q);var f = i.multiply(this.z).multiply(l.z).mod(this.curve.q);return new ECPointFp(this.curve, this.curve.fromBigInteger(a), this.curve.fromBigInteger(h), f);
    }function pointFpTwice() {
      if (this.isInfinity()) {
        return this;
      }if (this.y.toBigInteger().signum() == 0) {
        return this.curve.getInfinity();
      }var g = new BigInteger("3");var c = this.x.toBigInteger();var h = this.y.toBigInteger();var e = h.multiply(this.z);var j = e.multiply(h).mod(this.curve.q);var i = this.curve.a.toBigInteger();var k = c.square().multiply(g);if (!BigInteger.ZERO.equals(i)) {
        k = k.add(this.z.square().multiply(i));
      }k = k.mod(this.curve.q);var b = k.square().subtract(c.shiftLeft(3).multiply(j)).shiftLeft(1).multiply(e).mod(this.curve.q);var f = k.multiply(g).multiply(c).subtract(j.shiftLeft(1)).shiftLeft(2).multiply(j).subtract(k.square().multiply(k)).mod(this.curve.q);var d = e.square().multiply(e).shiftLeft(3).mod(this.curve.q);return new ECPointFp(this.curve, this.curve.fromBigInteger(b), this.curve.fromBigInteger(f), d);
    }function pointFpMultiply(b) {
      if (this.isInfinity()) {
        return this;
      }if (b.signum() == 0) {
        return this.curve.getInfinity();
      }var g = b;var f = g.multiply(new BigInteger("3"));var l = this.negate();var d = this;var c;for (c = f.bitLength() - 2; c > 0; --c) {
        d = d.twice();var a = f.testBit(c);var j = g.testBit(c);if (a != j) {
          d = d.add(a ? this : l);
        }
      }return d;
    }function pointFpMultiplyTwo(c, a, b) {
      var d;if (c.bitLength() > b.bitLength()) {
        d = c.bitLength() - 1;
      } else {
        d = b.bitLength() - 1;
      }var f = this.curve.getInfinity();var e = this.add(a);while (d >= 0) {
        f = f.twice();if (c.testBit(d)) {
          if (b.testBit(d)) {
            f = f.add(e);
          } else {
            f = f.add(this);
          }
        } else {
          if (b.testBit(d)) {
            f = f.add(a);
          }
        }--d;
      }return f;
    }ECPointFp.prototype.getX = pointFpGetX;ECPointFp.prototype.getY = pointFpGetY;ECPointFp.prototype.equals = pointFpEquals;ECPointFp.prototype.isInfinity = pointFpIsInfinity;ECPointFp.prototype.negate = pointFpNegate;ECPointFp.prototype.add = pointFpAdd;ECPointFp.prototype.twice = pointFpTwice;ECPointFp.prototype.multiply = pointFpMultiply;ECPointFp.prototype.multiplyTwo = pointFpMultiplyTwo;function ECCurveFp(e, d, c) {
      this.q = e;this.a = this.fromBigInteger(d);this.b = this.fromBigInteger(c);this.infinity = new ECPointFp(this, null, null);
    }function curveFpGetQ() {
      return this.q;
    }function curveFpGetA() {
      return this.a;
    }function curveFpGetB() {
      return this.b;
    }function curveFpEquals(a) {
      if (a == this) {
        return true;
      }return this.q.equals(a.q) && this.a.equals(a.a) && this.b.equals(a.b);
    }function curveFpGetInfinity() {
      return this.infinity;
    }function curveFpFromBigInteger(a) {
      return new ECFieldElementFp(this.q, a);
    }function curveFpDecodePointHex(d) {
      switch (parseInt(d.substr(0, 2), 16)) {case 0:
          return this.infinity;case 2:case 3:
          return null;case 4:case 6:case 7:
          var a = (d.length - 2) / 2;var c = d.substr(2, a);var b = d.substr(a + 2, a);return new ECPointFp(this, this.fromBigInteger(new BigInteger(c, 16)), this.fromBigInteger(new BigInteger(b, 16)));default:
          return null;}
    }ECCurveFp.prototype.getQ = curveFpGetQ;ECCurveFp.prototype.getA = curveFpGetA;ECCurveFp.prototype.getB = curveFpGetB;ECCurveFp.prototype.equals = curveFpEquals;ECCurveFp.prototype.getInfinity = curveFpGetInfinity;ECCurveFp.prototype.fromBigInteger = curveFpFromBigInteger;ECCurveFp.prototype.decodePointHex = curveFpDecodePointHex;
    /*! (c) Stefan Thomas | https://github.com/bitcoinjs/bitcoinjs-lib
     */
    ECFieldElementFp.prototype.getByteLength = function () {
      return Math.floor((this.toBigInteger().bitLength() + 7) / 8);
    };ECPointFp.prototype.getEncoded = function (c) {
      var d = function d(h, f) {
        var g = h.toByteArrayUnsigned();if (f < g.length) {
          g = g.slice(g.length - f);
        } else {
          while (f > g.length) {
            g.unshift(0);
          }
        }return g;
      };var a = this.getX().toBigInteger();var e = this.getY().toBigInteger();var b = d(a, 32);if (c) {
        if (e.isEven()) {
          b.unshift(2);
        } else {
          b.unshift(3);
        }
      } else {
        b.unshift(4);b = b.concat(d(e, 32));
      }return b;
    };ECPointFp.decodeFrom = function (g, c) {
      var f = c[0];var e = c.length - 1;var d = c.slice(1, 1 + e / 2);var b = c.slice(1 + e / 2, 1 + e);d.unshift(0);b.unshift(0);var a = new BigInteger(d);var h = new BigInteger(b);return new ECPointFp(g, g.fromBigInteger(a), g.fromBigInteger(h));
    };ECPointFp.decodeFromHex = function (g, c) {
      var f = c.substr(0, 2);var e = c.length - 2;var d = c.substr(2, e / 2);var b = c.substr(2 + e / 2, e / 2);var a = new BigInteger(d, 16);var h = new BigInteger(b, 16);return new ECPointFp(g, g.fromBigInteger(a), g.fromBigInteger(h));
    };ECPointFp.prototype.add2D = function (c) {
      if (this.isInfinity()) {
        return c;
      }if (c.isInfinity()) {
        return this;
      }if (this.x.equals(c.x)) {
        if (this.y.equals(c.y)) {
          return this.twice();
        }return this.curve.getInfinity();
      }var g = c.x.subtract(this.x);var e = c.y.subtract(this.y);var a = e.divide(g);var d = a.square().subtract(this.x).subtract(c.x);var f = a.multiply(this.x.subtract(d)).subtract(this.y);return new ECPointFp(this.curve, d, f);
    };ECPointFp.prototype.twice2D = function () {
      if (this.isInfinity()) {
        return this;
      }if (this.y.toBigInteger().signum() == 0) {
        return this.curve.getInfinity();
      }var b = this.curve.fromBigInteger(BigInteger.valueOf(2));var e = this.curve.fromBigInteger(BigInteger.valueOf(3));var a = this.x.square().multiply(e).add(this.curve.a).divide(this.y.multiply(b));var c = a.square().subtract(this.x.multiply(b));var d = a.multiply(this.x.subtract(c)).subtract(this.y);return new ECPointFp(this.curve, c, d);
    };ECPointFp.prototype.multiply2D = function (b) {
      if (this.isInfinity()) {
        return this;
      }if (b.signum() == 0) {
        return this.curve.getInfinity();
      }var g = b;var f = g.multiply(new BigInteger("3"));var l = this.negate();var d = this;var c;for (c = f.bitLength() - 2; c > 0; --c) {
        d = d.twice();var a = f.testBit(c);var j = g.testBit(c);if (a != j) {
          d = d.add2D(a ? this : l);
        }
      }return d;
    };ECPointFp.prototype.isOnCurve = function () {
      var d = this.getX().toBigInteger();var i = this.getY().toBigInteger();var f = this.curve.getA().toBigInteger();var c = this.curve.getB().toBigInteger();var h = this.curve.getQ();var e = i.multiply(i).mod(h);var g = d.multiply(d).multiply(d).add(f.multiply(d)).add(c).mod(h);return e.equals(g);
    };ECPointFp.prototype.toString = function () {
      return "(" + this.getX().toBigInteger().toString() + "," + this.getY().toBigInteger().toString() + ")";
    };ECPointFp.prototype.validate = function () {
      var c = this.curve.getQ();if (this.isInfinity()) {
        throw new Error("Point is at infinity.");
      }var a = this.getX().toBigInteger();var b = this.getY().toBigInteger();if (a.compareTo(BigInteger.ONE) < 0 || a.compareTo(c.subtract(BigInteger.ONE)) > 0) {
        throw new Error("x coordinate out of bounds");
      }if (b.compareTo(BigInteger.ONE) < 0 || b.compareTo(c.subtract(BigInteger.ONE)) > 0) {
        throw new Error("y coordinate out of bounds");
      }if (!this.isOnCurve()) {
        throw new Error("Point is not on the curve.");
      }if (this.multiply(c).isInfinity()) {
        throw new Error("Point is not a scalar multiple of G.");
      }return true;
    };
    /*! Mike Samuel (c) 2009 | code.google.com/p/json-sans-eval
     */
    var jsonParse = function () {
      var e = "(?:-?\\b(?:0|[1-9][0-9]*)(?:\\.[0-9]+)?(?:[eE][+-]?[0-9]+)?\\b)";var j = '(?:[^\\0-\\x08\\x0a-\\x1f"\\\\]|\\\\(?:["/\\\\bfnrt]|u[0-9A-Fa-f]{4}))';var i = '(?:"' + j + '*")';var d = new RegExp("(?:false|true|null|[\\{\\}\\[\\]]|" + e + "|" + i + ")", "g");var k = new RegExp("\\\\(?:([^u])|u(.{4}))", "g");var g = { '"': '"', "/": "/", "\\": "\\", b: "\b", f: "\f", n: "\n", r: "\r", t: "\t" };function h(l, m, n) {
        return m ? g[m] : String.fromCharCode(parseInt(n, 16));
      }var c = new String("");var a = "\\";var f = { "{": Object, "[": Array };var b = Object.hasOwnProperty;return function (u, q) {
        var p = u.match(d);var x;var v = p[0];var l = false;if ("{" === v) {
          x = {};
        } else {
          if ("[" === v) {
            x = [];
          } else {
            x = [];l = true;
          }
        }var t;var r = [x];for (var o = 1 - l, m = p.length; o < m; ++o) {
          v = p[o];var w;switch (v.charCodeAt(0)) {default:
              w = r[0];w[t || w.length] = +v;t = void 0;break;case 34:
              v = v.substring(1, v.length - 1);if (v.indexOf(a) !== -1) {
                v = v.replace(k, h);
              }w = r[0];if (!t) {
                if (w instanceof Array) {
                  t = w.length;
                } else {
                  t = v || c;break;
                }
              }w[t] = v;t = void 0;break;case 91:
              w = r[0];r.unshift(w[t || w.length] = []);t = void 0;break;case 93:
              r.shift();break;case 102:
              w = r[0];w[t || w.length] = false;t = void 0;break;case 110:
              w = r[0];w[t || w.length] = null;t = void 0;break;case 116:
              w = r[0];w[t || w.length] = true;t = void 0;break;case 123:
              w = r[0];r.unshift(w[t || w.length] = {});t = void 0;break;case 125:
              r.shift();break;}
        }if (l) {
          if (r.length !== 1) {
            throw new Error();
          }x = x[0];
        } else {
          if (r.length) {
            throw new Error();
          }
        }if (q) {
          var s = function s(C, B) {
            var D = C[B];if (D && (typeof D === "undefined" ? "undefined" : _typeof(D)) === "object") {
              var n = null;for (var z in D) {
                if (b.call(D, z) && D !== C) {
                  var y = s(D, z);if (y !== void 0) {
                    D[z] = y;
                  } else {
                    if (!n) {
                      n = [];
                    }n.push(z);
                  }
                }
              }if (n) {
                for (var A = n.length; --A >= 0;) {
                  delete D[n[A]];
                }
              }
            }return q.call(C, B, D);
          };x = s({ "": x }, "");
        }return x;
      };
    }();
    if (typeof KJUR == "undefined" || !KJUR) {
      exports.KJUR = KJUR = {};
    }if (typeof KJUR.asn1 == "undefined" || !KJUR.asn1) {
      KJUR.asn1 = {};
    }KJUR.asn1.ASN1Util = new function () {
      this.integerToByteHex = function (a) {
        var b = a.toString(16);if (b.length % 2 == 1) {
          b = "0" + b;
        }return b;
      };this.bigIntToMinTwosComplementsHex = function (j) {
        var f = j.toString(16);if (f.substr(0, 1) != "-") {
          if (f.length % 2 == 1) {
            f = "0" + f;
          } else {
            if (!f.match(/^[0-7]/)) {
              f = "00" + f;
            }
          }
        } else {
          var a = f.substr(1);var e = a.length;if (e % 2 == 1) {
            e += 1;
          } else {
            if (!f.match(/^[0-7]/)) {
              e += 2;
            }
          }var g = "";for (var d = 0; d < e; d++) {
            g += "f";
          }var c = new BigInteger(g, 16);var b = c.xor(j).add(BigInteger.ONE);f = b.toString(16).replace(/^-/, "");
        }return f;
      };this.getPEMStringFromHex = function (a, b) {
        return hextopem(a, b);
      };this.newObject = function (k) {
        var D = KJUR,
            n = D.asn1,
            z = n.DERBoolean,
            e = n.DERInteger,
            s = n.DERBitString,
            h = n.DEROctetString,
            v = n.DERNull,
            w = n.DERObjectIdentifier,
            l = n.DEREnumerated,
            g = n.DERUTF8String,
            f = n.DERNumericString,
            y = n.DERPrintableString,
            u = n.DERTeletexString,
            p = n.DERIA5String,
            C = n.DERUTCTime,
            j = n.DERGeneralizedTime,
            m = n.DERSequence,
            c = n.DERSet,
            r = n.DERTaggedObject,
            o = n.ASN1Util.newObject;var t = Object.keys(k);if (t.length != 1) {
          throw "key of param shall be only one.";
        }var F = t[0];if (":bool:int:bitstr:octstr:null:oid:enum:utf8str:numstr:prnstr:telstr:ia5str:utctime:gentime:seq:set:tag:".indexOf(":" + F + ":") == -1) {
          throw "undefined key: " + F;
        }if (F == "bool") {
          return new z(k[F]);
        }if (F == "int") {
          return new e(k[F]);
        }if (F == "bitstr") {
          return new s(k[F]);
        }if (F == "octstr") {
          return new h(k[F]);
        }if (F == "null") {
          return new v(k[F]);
        }if (F == "oid") {
          return new w(k[F]);
        }if (F == "enum") {
          return new l(k[F]);
        }if (F == "utf8str") {
          return new g(k[F]);
        }if (F == "numstr") {
          return new f(k[F]);
        }if (F == "prnstr") {
          return new y(k[F]);
        }if (F == "telstr") {
          return new u(k[F]);
        }if (F == "ia5str") {
          return new p(k[F]);
        }if (F == "utctime") {
          return new C(k[F]);
        }if (F == "gentime") {
          return new j(k[F]);
        }if (F == "seq") {
          var d = k[F];var E = [];for (var x = 0; x < d.length; x++) {
            var B = o(d[x]);E.push(B);
          }return new m({ array: E });
        }if (F == "set") {
          var d = k[F];var E = [];for (var x = 0; x < d.length; x++) {
            var B = o(d[x]);E.push(B);
          }return new c({ array: E });
        }if (F == "tag") {
          var A = k[F];if (Object.prototype.toString.call(A) === "[object Array]" && A.length == 3) {
            var q = o(A[2]);return new r({ tag: A[0], explicit: A[1], obj: q });
          } else {
            var b = {};if (A.explicit !== undefined) {
              b.explicit = A.explicit;
            }if (A.tag !== undefined) {
              b.tag = A.tag;
            }if (A.obj === undefined) {
              throw "obj shall be specified for 'tag'.";
            }b.obj = o(A.obj);return new r(b);
          }
        }
      };this.jsonToASN1HEX = function (b) {
        var a = this.newObject(b);return a.getEncodedHex();
      };
    }();KJUR.asn1.ASN1Util.oidHexToInt = function (a) {
      var j = "";var k = parseInt(a.substr(0, 2), 16);var d = Math.floor(k / 40);var c = k % 40;var j = d + "." + c;var e = "";for (var f = 2; f < a.length; f += 2) {
        var g = parseInt(a.substr(f, 2), 16);var h = ("00000000" + g.toString(2)).slice(-8);e = e + h.substr(1, 7);if (h.substr(0, 1) == "0") {
          var b = new BigInteger(e, 2);j = j + "." + b.toString(10);e = "";
        }
      }return j;
    };KJUR.asn1.ASN1Util.oidIntToHex = function (f) {
      var e = function e(a) {
        var k = a.toString(16);if (k.length == 1) {
          k = "0" + k;
        }return k;
      };var d = function d(o) {
        var n = "";var k = new BigInteger(o, 10);var a = k.toString(2);var l = 7 - a.length % 7;if (l == 7) {
          l = 0;
        }var q = "";for (var m = 0; m < l; m++) {
          q += "0";
        }a = q + a;for (var m = 0; m < a.length - 1; m += 7) {
          var p = a.substr(m, 7);if (m != a.length - 7) {
            p = "1" + p;
          }n += e(parseInt(p, 2));
        }return n;
      };if (!f.match(/^[0-9.]+$/)) {
        throw "malformed oid string: " + f;
      }var g = "";var b = f.split(".");var j = parseInt(b[0]) * 40 + parseInt(b[1]);g += e(j);b.splice(0, 2);for (var c = 0; c < b.length; c++) {
        g += d(b[c]);
      }return g;
    };KJUR.asn1.ASN1Object = function () {
      var c = true;var b = null;var d = "00";var e = "00";var a = "";this.getLengthHexFromValue = function () {
        if (typeof this.hV == "undefined" || this.hV == null) {
          throw "this.hV is null or undefined.";
        }if (this.hV.length % 2 == 1) {
          throw "value hex must be even length: n=" + a.length + ",v=" + this.hV;
        }var i = this.hV.length / 2;var h = i.toString(16);if (h.length % 2 == 1) {
          h = "0" + h;
        }if (i < 128) {
          return h;
        } else {
          var g = h.length / 2;if (g > 15) {
            throw "ASN.1 length too long to represent by 8x: n = " + i.toString(16);
          }var f = 128 + g;return f.toString(16) + h;
        }
      };this.getEncodedHex = function () {
        if (this.hTLV == null || this.isModified) {
          this.hV = this.getFreshValueHex();this.hL = this.getLengthHexFromValue();this.hTLV = this.hT + this.hL + this.hV;this.isModified = false;
        }return this.hTLV;
      };this.getValueHex = function () {
        this.getEncodedHex();return this.hV;
      };this.getFreshValueHex = function () {
        return "";
      };
    };KJUR.asn1.DERAbstractString = function (c) {
      KJUR.asn1.DERAbstractString.superclass.constructor.call(this);var b = null;var a = null;this.getString = function () {
        return this.s;
      };this.setString = function (d) {
        this.hTLV = null;this.isModified = true;this.s = d;this.hV = utf8tohex(this.s).toLowerCase();
      };this.setStringHex = function (d) {
        this.hTLV = null;this.isModified = true;this.s = null;this.hV = d;
      };this.getFreshValueHex = function () {
        return this.hV;
      };if (typeof c != "undefined") {
        if (typeof c == "string") {
          this.setString(c);
        } else {
          if (typeof c.str != "undefined") {
            this.setString(c.str);
          } else {
            if (typeof c.hex != "undefined") {
              this.setStringHex(c.hex);
            }
          }
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERAbstractString, KJUR.asn1.ASN1Object);KJUR.asn1.DERAbstractTime = function (c) {
      KJUR.asn1.DERAbstractTime.superclass.constructor.call(this);var b = null;var a = null;this.localDateToUTC = function (f) {
        utc = f.getTime() + f.getTimezoneOffset() * 60000;var e = new Date(utc);return e;
      };this.formatDate = function (m, o, e) {
        var g = this.zeroPadding;var n = this.localDateToUTC(m);var p = String(n.getFullYear());if (o == "utc") {
          p = p.substr(2, 2);
        }var l = g(String(n.getMonth() + 1), 2);var q = g(String(n.getDate()), 2);var h = g(String(n.getHours()), 2);var i = g(String(n.getMinutes()), 2);var j = g(String(n.getSeconds()), 2);var r = p + l + q + h + i + j;if (e === true) {
          var f = n.getMilliseconds();if (f != 0) {
            var k = g(String(f), 3);k = k.replace(/[0]+$/, "");r = r + "." + k;
          }
        }return r + "Z";
      };this.zeroPadding = function (e, d) {
        if (e.length >= d) {
          return e;
        }return new Array(d - e.length + 1).join("0") + e;
      };this.getString = function () {
        return this.s;
      };this.setString = function (d) {
        this.hTLV = null;this.isModified = true;this.s = d;this.hV = stohex(d);
      };this.setByDateValue = function (h, j, e, d, f, g) {
        var i = new Date(Date.UTC(h, j - 1, e, d, f, g, 0));this.setByDate(i);
      };this.getFreshValueHex = function () {
        return this.hV;
      };
    };YAHOO.lang.extend(KJUR.asn1.DERAbstractTime, KJUR.asn1.ASN1Object);KJUR.asn1.DERAbstractStructured = function (b) {
      KJUR.asn1.DERAbstractString.superclass.constructor.call(this);var a = null;this.setByASN1ObjectArray = function (c) {
        this.hTLV = null;this.isModified = true;this.asn1Array = c;
      };this.appendASN1Object = function (c) {
        this.hTLV = null;this.isModified = true;this.asn1Array.push(c);
      };this.asn1Array = new Array();if (typeof b != "undefined") {
        if (typeof b.array != "undefined") {
          this.asn1Array = b.array;
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERAbstractStructured, KJUR.asn1.ASN1Object);KJUR.asn1.DERBoolean = function () {
      KJUR.asn1.DERBoolean.superclass.constructor.call(this);this.hT = "01";this.hTLV = "0101ff";
    };YAHOO.lang.extend(KJUR.asn1.DERBoolean, KJUR.asn1.ASN1Object);KJUR.asn1.DERInteger = function (a) {
      KJUR.asn1.DERInteger.superclass.constructor.call(this);this.hT = "02";this.setByBigInteger = function (b) {
        this.hTLV = null;this.isModified = true;this.hV = KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(b);
      };this.setByInteger = function (c) {
        var b = new BigInteger(String(c), 10);this.setByBigInteger(b);
      };this.setValueHex = function (b) {
        this.hV = b;
      };this.getFreshValueHex = function () {
        return this.hV;
      };if (typeof a != "undefined") {
        if (typeof a.bigint != "undefined") {
          this.setByBigInteger(a.bigint);
        } else {
          if (typeof a["int"] != "undefined") {
            this.setByInteger(a["int"]);
          } else {
            if (typeof a == "number") {
              this.setByInteger(a);
            } else {
              if (typeof a.hex != "undefined") {
                this.setValueHex(a.hex);
              }
            }
          }
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERInteger, KJUR.asn1.ASN1Object);KJUR.asn1.DERBitString = function (b) {
      if (b !== undefined && typeof b.obj !== "undefined") {
        var a = KJUR.asn1.ASN1Util.newObject(b.obj);b.hex = "00" + a.getEncodedHex();
      }KJUR.asn1.DERBitString.superclass.constructor.call(this);this.hT = "03";this.setHexValueIncludingUnusedBits = function (c) {
        this.hTLV = null;this.isModified = true;this.hV = c;
      };this.setUnusedBitsAndHexValue = function (c, e) {
        if (c < 0 || 7 < c) {
          throw "unused bits shall be from 0 to 7: u = " + c;
        }var d = "0" + c;this.hTLV = null;this.isModified = true;this.hV = d + e;
      };this.setByBinaryString = function (e) {
        e = e.replace(/0+$/, "");var f = 8 - e.length % 8;if (f == 8) {
          f = 0;
        }for (var g = 0; g <= f; g++) {
          e += "0";
        }var j = "";for (var g = 0; g < e.length - 1; g += 8) {
          var d = e.substr(g, 8);var c = parseInt(d, 2).toString(16);if (c.length == 1) {
            c = "0" + c;
          }j += c;
        }this.hTLV = null;this.isModified = true;this.hV = "0" + f + j;
      };this.setByBooleanArray = function (e) {
        var d = "";for (var c = 0; c < e.length; c++) {
          if (e[c] == true) {
            d += "1";
          } else {
            d += "0";
          }
        }this.setByBinaryString(d);
      };this.newFalseArray = function (e) {
        var c = new Array(e);for (var d = 0; d < e; d++) {
          c[d] = false;
        }return c;
      };this.getFreshValueHex = function () {
        return this.hV;
      };if (typeof b != "undefined") {
        if (typeof b == "string" && b.toLowerCase().match(/^[0-9a-f]+$/)) {
          this.setHexValueIncludingUnusedBits(b);
        } else {
          if (typeof b.hex != "undefined") {
            this.setHexValueIncludingUnusedBits(b.hex);
          } else {
            if (typeof b.bin != "undefined") {
              this.setByBinaryString(b.bin);
            } else {
              if (typeof b.array != "undefined") {
                this.setByBooleanArray(b.array);
              }
            }
          }
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERBitString, KJUR.asn1.ASN1Object);KJUR.asn1.DEROctetString = function (b) {
      if (b !== undefined && typeof b.obj !== "undefined") {
        var a = KJUR.asn1.ASN1Util.newObject(b.obj);b.hex = a.getEncodedHex();
      }KJUR.asn1.DEROctetString.superclass.constructor.call(this, b);this.hT = "04";
    };YAHOO.lang.extend(KJUR.asn1.DEROctetString, KJUR.asn1.DERAbstractString);KJUR.asn1.DERNull = function () {
      KJUR.asn1.DERNull.superclass.constructor.call(this);this.hT = "05";this.hTLV = "0500";
    };YAHOO.lang.extend(KJUR.asn1.DERNull, KJUR.asn1.ASN1Object);KJUR.asn1.DERObjectIdentifier = function (c) {
      var b = function b(d) {
        var e = d.toString(16);if (e.length == 1) {
          e = "0" + e;
        }return e;
      };var a = function a(k) {
        var j = "";var e = new BigInteger(k, 10);var d = e.toString(2);var f = 7 - d.length % 7;if (f == 7) {
          f = 0;
        }var m = "";for (var g = 0; g < f; g++) {
          m += "0";
        }d = m + d;for (var g = 0; g < d.length - 1; g += 7) {
          var l = d.substr(g, 7);if (g != d.length - 7) {
            l = "1" + l;
          }j += b(parseInt(l, 2));
        }return j;
      };KJUR.asn1.DERObjectIdentifier.superclass.constructor.call(this);this.hT = "06";this.setValueHex = function (d) {
        this.hTLV = null;this.isModified = true;this.s = null;this.hV = d;
      };this.setValueOidString = function (f) {
        if (!f.match(/^[0-9.]+$/)) {
          throw "malformed oid string: " + f;
        }var g = "";var d = f.split(".");var j = parseInt(d[0]) * 40 + parseInt(d[1]);g += b(j);d.splice(0, 2);for (var e = 0; e < d.length; e++) {
          g += a(d[e]);
        }this.hTLV = null;this.isModified = true;this.s = null;this.hV = g;
      };this.setValueName = function (e) {
        var d = KJUR.asn1.x509.OID.name2oid(e);if (d !== "") {
          this.setValueOidString(d);
        } else {
          throw "DERObjectIdentifier oidName undefined: " + e;
        }
      };this.getFreshValueHex = function () {
        return this.hV;
      };if (c !== undefined) {
        if (typeof c === "string") {
          if (c.match(/^[0-2].[0-9.]+$/)) {
            this.setValueOidString(c);
          } else {
            this.setValueName(c);
          }
        } else {
          if (c.oid !== undefined) {
            this.setValueOidString(c.oid);
          } else {
            if (c.hex !== undefined) {
              this.setValueHex(c.hex);
            } else {
              if (c.name !== undefined) {
                this.setValueName(c.name);
              }
            }
          }
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERObjectIdentifier, KJUR.asn1.ASN1Object);KJUR.asn1.DEREnumerated = function (a) {
      KJUR.asn1.DEREnumerated.superclass.constructor.call(this);this.hT = "0a";this.setByBigInteger = function (b) {
        this.hTLV = null;this.isModified = true;this.hV = KJUR.asn1.ASN1Util.bigIntToMinTwosComplementsHex(b);
      };this.setByInteger = function (c) {
        var b = new BigInteger(String(c), 10);this.setByBigInteger(b);
      };this.setValueHex = function (b) {
        this.hV = b;
      };this.getFreshValueHex = function () {
        return this.hV;
      };if (typeof a != "undefined") {
        if (typeof a["int"] != "undefined") {
          this.setByInteger(a["int"]);
        } else {
          if (typeof a == "number") {
            this.setByInteger(a);
          } else {
            if (typeof a.hex != "undefined") {
              this.setValueHex(a.hex);
            }
          }
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DEREnumerated, KJUR.asn1.ASN1Object);KJUR.asn1.DERUTF8String = function (a) {
      KJUR.asn1.DERUTF8String.superclass.constructor.call(this, a);this.hT = "0c";
    };YAHOO.lang.extend(KJUR.asn1.DERUTF8String, KJUR.asn1.DERAbstractString);KJUR.asn1.DERNumericString = function (a) {
      KJUR.asn1.DERNumericString.superclass.constructor.call(this, a);this.hT = "12";
    };YAHOO.lang.extend(KJUR.asn1.DERNumericString, KJUR.asn1.DERAbstractString);KJUR.asn1.DERPrintableString = function (a) {
      KJUR.asn1.DERPrintableString.superclass.constructor.call(this, a);this.hT = "13";
    };YAHOO.lang.extend(KJUR.asn1.DERPrintableString, KJUR.asn1.DERAbstractString);KJUR.asn1.DERTeletexString = function (a) {
      KJUR.asn1.DERTeletexString.superclass.constructor.call(this, a);this.hT = "14";
    };YAHOO.lang.extend(KJUR.asn1.DERTeletexString, KJUR.asn1.DERAbstractString);KJUR.asn1.DERIA5String = function (a) {
      KJUR.asn1.DERIA5String.superclass.constructor.call(this, a);this.hT = "16";
    };YAHOO.lang.extend(KJUR.asn1.DERIA5String, KJUR.asn1.DERAbstractString);KJUR.asn1.DERUTCTime = function (a) {
      KJUR.asn1.DERUTCTime.superclass.constructor.call(this, a);this.hT = "17";this.setByDate = function (b) {
        this.hTLV = null;this.isModified = true;this.date = b;this.s = this.formatDate(this.date, "utc");this.hV = stohex(this.s);
      };this.getFreshValueHex = function () {
        if (typeof this.date == "undefined" && typeof this.s == "undefined") {
          this.date = new Date();this.s = this.formatDate(this.date, "utc");this.hV = stohex(this.s);
        }return this.hV;
      };if (a !== undefined) {
        if (a.str !== undefined) {
          this.setString(a.str);
        } else {
          if (typeof a == "string" && a.match(/^[0-9]{12}Z$/)) {
            this.setString(a);
          } else {
            if (a.hex !== undefined) {
              this.setStringHex(a.hex);
            } else {
              if (a.date !== undefined) {
                this.setByDate(a.date);
              }
            }
          }
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERUTCTime, KJUR.asn1.DERAbstractTime);KJUR.asn1.DERGeneralizedTime = function (a) {
      KJUR.asn1.DERGeneralizedTime.superclass.constructor.call(this, a);this.hT = "18";this.withMillis = false;this.setByDate = function (b) {
        this.hTLV = null;this.isModified = true;this.date = b;this.s = this.formatDate(this.date, "gen", this.withMillis);this.hV = stohex(this.s);
      };this.getFreshValueHex = function () {
        if (this.date === undefined && this.s === undefined) {
          this.date = new Date();this.s = this.formatDate(this.date, "gen", this.withMillis);this.hV = stohex(this.s);
        }return this.hV;
      };if (a !== undefined) {
        if (a.str !== undefined) {
          this.setString(a.str);
        } else {
          if (typeof a == "string" && a.match(/^[0-9]{14}Z$/)) {
            this.setString(a);
          } else {
            if (a.hex !== undefined) {
              this.setStringHex(a.hex);
            } else {
              if (a.date !== undefined) {
                this.setByDate(a.date);
              }
            }
          }
        }if (a.millis === true) {
          this.withMillis = true;
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERGeneralizedTime, KJUR.asn1.DERAbstractTime);KJUR.asn1.DERSequence = function (a) {
      KJUR.asn1.DERSequence.superclass.constructor.call(this, a);this.hT = "30";this.getFreshValueHex = function () {
        var c = "";for (var b = 0; b < this.asn1Array.length; b++) {
          var d = this.asn1Array[b];c += d.getEncodedHex();
        }this.hV = c;return this.hV;
      };
    };YAHOO.lang.extend(KJUR.asn1.DERSequence, KJUR.asn1.DERAbstractStructured);KJUR.asn1.DERSet = function (a) {
      KJUR.asn1.DERSet.superclass.constructor.call(this, a);this.hT = "31";this.sortFlag = true;this.getFreshValueHex = function () {
        var b = new Array();for (var c = 0; c < this.asn1Array.length; c++) {
          var d = this.asn1Array[c];b.push(d.getEncodedHex());
        }if (this.sortFlag == true) {
          b.sort();
        }this.hV = b.join("");return this.hV;
      };if (typeof a != "undefined") {
        if (typeof a.sortflag != "undefined" && a.sortflag == false) {
          this.sortFlag = false;
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERSet, KJUR.asn1.DERAbstractStructured);KJUR.asn1.DERTaggedObject = function (a) {
      KJUR.asn1.DERTaggedObject.superclass.constructor.call(this);this.hT = "a0";this.hV = "";this.isExplicit = true;this.asn1Object = null;this.setASN1Object = function (b, c, d) {
        this.hT = c;this.isExplicit = b;this.asn1Object = d;if (this.isExplicit) {
          this.hV = this.asn1Object.getEncodedHex();this.hTLV = null;this.isModified = true;
        } else {
          this.hV = null;this.hTLV = d.getEncodedHex();this.hTLV = this.hTLV.replace(/^../, c);this.isModified = false;
        }
      };this.getFreshValueHex = function () {
        return this.hV;
      };if (typeof a != "undefined") {
        if (typeof a.tag != "undefined") {
          this.hT = a.tag;
        }if (typeof a.explicit != "undefined") {
          this.isExplicit = a.explicit;
        }if (typeof a.obj != "undefined") {
          this.asn1Object = a.obj;this.setASN1Object(this.isExplicit, this.hT, this.asn1Object);
        }
      }
    };YAHOO.lang.extend(KJUR.asn1.DERTaggedObject, KJUR.asn1.ASN1Object);
    var ASN1HEX = new function () {}();ASN1HEX.getLblen = function (c, a) {
      if (c.substr(a + 2, 1) != "8") {
        return 1;
      }var b = parseInt(c.substr(a + 3, 1));if (b == 0) {
        return -1;
      }if (0 < b && b < 10) {
        return b + 1;
      }return -2;
    };ASN1HEX.getL = function (c, b) {
      var a = ASN1HEX.getLblen(c, b);if (a < 1) {
        return "";
      }return c.substr(b + 2, a * 2);
    };ASN1HEX.getVblen = function (d, a) {
      var c, b;c = ASN1HEX.getL(d, a);if (c == "") {
        return -1;
      }if (c.substr(0, 1) === "8") {
        b = new BigInteger(c.substr(2), 16);
      } else {
        b = new BigInteger(c, 16);
      }return b.intValue();
    };ASN1HEX.getVidx = function (c, b) {
      var a = ASN1HEX.getLblen(c, b);if (a < 0) {
        return a;
      }return b + (a + 1) * 2;
    };ASN1HEX.getV = function (d, a) {
      var c = ASN1HEX.getVidx(d, a);var b = ASN1HEX.getVblen(d, a);return d.substr(c, b * 2);
    };ASN1HEX.getTLV = function (b, a) {
      return b.substr(a, 2) + ASN1HEX.getL(b, a) + ASN1HEX.getV(b, a);
    };ASN1HEX.getNextSiblingIdx = function (d, a) {
      var c = ASN1HEX.getVidx(d, a);var b = ASN1HEX.getVblen(d, a);return c + b * 2;
    };ASN1HEX.getChildIdx = function (e, f) {
      var j = ASN1HEX;var g = new Array();var i = j.getVidx(e, f);if (e.substr(f, 2) == "03") {
        g.push(i + 2);
      } else {
        g.push(i);
      }var l = j.getVblen(e, f);var c = i;var d = 0;while (1) {
        var b = j.getNextSiblingIdx(e, c);if (b == null || b - i >= l * 2) {
          break;
        }if (d >= 200) {
          break;
        }g.push(b);c = b;d++;
      }return g;
    };ASN1HEX.getNthChildIdx = function (d, b, e) {
      var c = ASN1HEX.getChildIdx(d, b);return c[e];
    };ASN1HEX.getIdxbyList = function (e, d, c, i) {
      var g = ASN1HEX;var f, b;if (c.length == 0) {
        if (i !== undefined) {
          if (e.substr(d, 2) !== i) {
            throw "checking tag doesn't match: " + e.substr(d, 2) + "!=" + i;
          }
        }return d;
      }f = c.shift();b = g.getChildIdx(e, d);return g.getIdxbyList(e, b[f], c, i);
    };ASN1HEX.getTLVbyList = function (d, c, b, f) {
      var e = ASN1HEX;var a = e.getIdxbyList(d, c, b);if (a === undefined) {
        throw "can't find nthList object";
      }if (f !== undefined) {
        if (d.substr(a, 2) != f) {
          throw "checking tag doesn't match: " + d.substr(a, 2) + "!=" + f;
        }
      }return e.getTLV(d, a);
    };ASN1HEX.getVbyList = function (e, c, b, g, i) {
      var f = ASN1HEX;var a, d;a = f.getIdxbyList(e, c, b, g);if (a === undefined) {
        throw "can't find nthList object";
      }d = f.getV(e, a);if (i === true) {
        d = d.substr(2);
      }return d;
    };ASN1HEX.hextooidstr = function (e) {
      var h = function h(b, a) {
        if (b.length >= a) {
          return b;
        }return new Array(a - b.length + 1).join("0") + b;
      };var l = [];var o = e.substr(0, 2);var f = parseInt(o, 16);l[0] = new String(Math.floor(f / 40));l[1] = new String(f % 40);var m = e.substr(2);var k = [];for (var g = 0; g < m.length / 2; g++) {
        k.push(parseInt(m.substr(g * 2, 2), 16));
      }var j = [];var d = "";for (var g = 0; g < k.length; g++) {
        if (k[g] & 128) {
          d = d + h((k[g] & 127).toString(2), 7);
        } else {
          d = d + h((k[g] & 127).toString(2), 7);j.push(new String(parseInt(d, 2)));d = "";
        }
      }var n = l.join(".");if (j.length > 0) {
        n = n + "." + j.join(".");
      }return n;
    };ASN1HEX.dump = function (t, c, l, g) {
      var p = ASN1HEX;var j = p.getV;var y = p.dump;var w = p.getChildIdx;var e = t;if (t instanceof KJUR.asn1.ASN1Object) {
        e = t.getEncodedHex();
      }var q = function q(A, i) {
        if (A.length <= i * 2) {
          return A;
        } else {
          var v = A.substr(0, i) + "..(total " + A.length / 2 + "bytes).." + A.substr(A.length - i, i);return v;
        }
      };if (c === undefined) {
        c = { ommit_long_octet: 32 };
      }if (l === undefined) {
        l = 0;
      }if (g === undefined) {
        g = "";
      }var x = c.ommit_long_octet;if (e.substr(l, 2) == "01") {
        var h = j(e, l);if (h == "00") {
          return g + "BOOLEAN FALSE\n";
        } else {
          return g + "BOOLEAN TRUE\n";
        }
      }if (e.substr(l, 2) == "02") {
        var h = j(e, l);return g + "INTEGER " + q(h, x) + "\n";
      }if (e.substr(l, 2) == "03") {
        var h = j(e, l);return g + "BITSTRING " + q(h, x) + "\n";
      }if (e.substr(l, 2) == "04") {
        var h = j(e, l);if (p.isASN1HEX(h)) {
          var k = g + "OCTETSTRING, encapsulates\n";k = k + y(h, c, 0, g + "  ");return k;
        } else {
          return g + "OCTETSTRING " + q(h, x) + "\n";
        }
      }if (e.substr(l, 2) == "05") {
        return g + "NULL\n";
      }if (e.substr(l, 2) == "06") {
        var m = j(e, l);var a = KJUR.asn1.ASN1Util.oidHexToInt(m);var o = KJUR.asn1.x509.OID.oid2name(a);var b = a.replace(/\./g, " ");if (o != "") {
          return g + "ObjectIdentifier " + o + " (" + b + ")\n";
        } else {
          return g + "ObjectIdentifier (" + b + ")\n";
        }
      }if (e.substr(l, 2) == "0c") {
        return g + "UTF8String '" + hextoutf8(j(e, l)) + "'\n";
      }if (e.substr(l, 2) == "13") {
        return g + "PrintableString '" + hextoutf8(j(e, l)) + "'\n";
      }if (e.substr(l, 2) == "14") {
        return g + "TeletexString '" + hextoutf8(j(e, l)) + "'\n";
      }if (e.substr(l, 2) == "16") {
        return g + "IA5String '" + hextoutf8(j(e, l)) + "'\n";
      }if (e.substr(l, 2) == "17") {
        return g + "UTCTime " + hextoutf8(j(e, l)) + "\n";
      }if (e.substr(l, 2) == "18") {
        return g + "GeneralizedTime " + hextoutf8(j(e, l)) + "\n";
      }if (e.substr(l, 2) == "30") {
        if (e.substr(l, 4) == "3000") {
          return g + "SEQUENCE {}\n";
        }var k = g + "SEQUENCE\n";var d = w(e, l);var f = c;if ((d.length == 2 || d.length == 3) && e.substr(d[0], 2) == "06" && e.substr(d[d.length - 1], 2) == "04") {
          var o = p.oidname(j(e, d[0]));var r = JSON.parse(JSON.stringify(c));r.x509ExtName = o;f = r;
        }for (var u = 0; u < d.length; u++) {
          k = k + y(e, f, d[u], g + "  ");
        }return k;
      }if (e.substr(l, 2) == "31") {
        var k = g + "SET\n";var d = w(e, l);for (var u = 0; u < d.length; u++) {
          k = k + y(e, c, d[u], g + "  ");
        }return k;
      }var z = parseInt(e.substr(l, 2), 16);if ((z & 128) != 0) {
        var n = z & 31;if ((z & 32) != 0) {
          var k = g + "[" + n + "]\n";var d = w(e, l);for (var u = 0; u < d.length; u++) {
            k = k + y(e, c, d[u], g + "  ");
          }return k;
        } else {
          var h = j(e, l);if (h.substr(0, 8) == "68747470") {
            h = hextoutf8(h);
          }if (c.x509ExtName === "subjectAltName" && n == 2) {
            h = hextoutf8(h);
          }var k = g + "[" + n + "] " + h + "\n";return k;
        }
      }return g + "UNKNOWN(" + e.substr(l, 2) + ") " + j(e, l) + "\n";
    };ASN1HEX.isASN1HEX = function (e) {
      var d = ASN1HEX;if (e.length % 2 == 1) {
        return false;
      }var c = d.getVblen(e, 0);var b = e.substr(0, 2);var f = d.getL(e, 0);var a = e.length - b.length - f.length;if (a == c * 2) {
        return true;
      }return false;
    };ASN1HEX.oidname = function (a) {
      var c = KJUR.asn1;if (KJUR.lang.String.isHex(a)) {
        a = c.ASN1Util.oidHexToInt(a);
      }var b = c.x509.OID.oid2name(a);if (b === "") {
        b = a;
      }return b;
    };
    var KJUR;if (typeof KJUR == "undefined" || !KJUR) {
      exports.KJUR = KJUR = {};
    }if (typeof KJUR.lang == "undefined" || !KJUR.lang) {
      KJUR.lang = {};
    }KJUR.lang.String = function () {};function Base64x() {}function stoBA(d) {
      var b = new Array();for (var c = 0; c < d.length; c++) {
        b[c] = d.charCodeAt(c);
      }return b;
    }function BAtos(b) {
      var d = "";for (var c = 0; c < b.length; c++) {
        d = d + String.fromCharCode(b[c]);
      }return d;
    }function BAtohex(b) {
      var e = "";for (var d = 0; d < b.length; d++) {
        var c = b[d].toString(16);if (c.length == 1) {
          c = "0" + c;
        }e = e + c;
      }return e;
    }function stohex(a) {
      return BAtohex(stoBA(a));
    }function stob64(a) {
      return hex2b64(stohex(a));
    }function stob64u(a) {
      return b64tob64u(hex2b64(stohex(a)));
    }function b64utos(a) {
      return BAtos(b64toBA(b64utob64(a)));
    }function b64tob64u(a) {
      a = a.replace(/\=/g, "");a = a.replace(/\+/g, "-");a = a.replace(/\//g, "_");return a;
    }function b64utob64(a) {
      if (a.length % 4 == 2) {
        a = a + "==";
      } else {
        if (a.length % 4 == 3) {
          a = a + "=";
        }
      }a = a.replace(/-/g, "+");a = a.replace(/_/g, "/");return a;
    }function hextob64u(a) {
      if (a.length % 2 == 1) {
        a = "0" + a;
      }return b64tob64u(hex2b64(a));
    }function b64utohex(a) {
      return b64tohex(b64utob64(a));
    }var utf8tob64u, b64utoutf8;if (typeof Buffer === "function") {
      exports.utf8tob64u = utf8tob64u = function utf8tob64u(a) {
        return b64tob64u(new Buffer(a, "utf8").toString("base64"));
      };exports.b64utoutf8 = b64utoutf8 = function b64utoutf8(a) {
        return new Buffer(b64utob64(a), "base64").toString("utf8");
      };
    } else {
      exports.utf8tob64u = utf8tob64u = function utf8tob64u(a) {
        return hextob64u(uricmptohex(encodeURIComponentAll(a)));
      };exports.b64utoutf8 = b64utoutf8 = function b64utoutf8(a) {
        return decodeURIComponent(hextouricmp(b64utohex(a)));
      };
    }function utf8tob64(a) {
      return hex2b64(uricmptohex(encodeURIComponentAll(a)));
    }function b64toutf8(a) {
      return decodeURIComponent(hextouricmp(b64tohex(a)));
    }function utf8tohex(a) {
      return uricmptohex(encodeURIComponentAll(a));
    }function hextoutf8(a) {
      return decodeURIComponent(hextouricmp(a));
    }function hextorstr(c) {
      var b = "";for (var a = 0; a < c.length - 1; a += 2) {
        b += String.fromCharCode(parseInt(c.substr(a, 2), 16));
      }return b;
    }function rstrtohex(c) {
      var a = "";for (var b = 0; b < c.length; b++) {
        a += ("0" + c.charCodeAt(b).toString(16)).slice(-2);
      }return a;
    }function hextob64(a) {
      return hex2b64(a);
    }function hextob64nl(b) {
      var a = hextob64(b);var c = a.replace(/(.{64})/g, "$1\r\n");c = c.replace(/\r\n$/, "");return c;
    }function b64nltohex(b) {
      var a = b.replace(/[^0-9A-Za-z\/+=]*/g, "");var c = b64tohex(a);return c;
    }function hextopem(a, b) {
      var c = hextob64nl(a);return "-----BEGIN " + b + "-----\r\n" + c + "\r\n-----END " + b + "-----\r\n";
    }function pemtohex(a, b) {
      if (a.indexOf("-----BEGIN ") == -1) {
        throw "can't find PEM header: " + b;
      }if (b !== undefined) {
        a = a.replace("-----BEGIN " + b + "-----", "");a = a.replace("-----END " + b + "-----", "");
      } else {
        a = a.replace(/-----BEGIN [^-]+-----/, "");a = a.replace(/-----END [^-]+-----/, "");
      }return b64nltohex(a);
    }function hextoArrayBuffer(d) {
      if (d.length % 2 != 0) {
        throw "input is not even length";
      }if (d.match(/^[0-9A-Fa-f]+$/) == null) {
        throw "input is not hexadecimal";
      }var b = new ArrayBuffer(d.length / 2);var a = new DataView(b);for (var c = 0; c < d.length / 2; c++) {
        a.setUint8(c, parseInt(d.substr(c * 2, 2), 16));
      }return b;
    }function ArrayBuffertohex(b) {
      var d = "";var a = new DataView(b);for (var c = 0; c < b.byteLength; c++) {
        d += ("00" + a.getUint8(c).toString(16)).slice(-2);
      }return d;
    }function zulutomsec(n) {
      var l, j, m, e, f, i, b, k;var a, h, g, c;c = n.match(/^(\d{2}|\d{4})(\d\d)(\d\d)(\d\d)(\d\d)(\d\d)(|\.\d+)Z$/);if (c) {
        a = c[1];l = parseInt(a);if (a.length === 2) {
          if (50 <= l && l < 100) {
            l = 1900 + l;
          } else {
            if (0 <= l && l < 50) {
              l = 2000 + l;
            }
          }
        }j = parseInt(c[2]) - 1;m = parseInt(c[3]);e = parseInt(c[4]);f = parseInt(c[5]);i = parseInt(c[6]);b = 0;h = c[7];if (h !== "") {
          g = (h.substr(1) + "00").substr(0, 3);b = parseInt(g);
        }return Date.UTC(l, j, m, e, f, i, b);
      }throw "unsupported zulu format: " + n;
    }function zulutosec(a) {
      var b = zulutomsec(a);return ~~(b / 1000);
    }function zulutodate(a) {
      return new Date(zulutomsec(a));
    }function datetozulu(g, e, f) {
      var b;var a = g.getUTCFullYear();if (e) {
        if (a < 1950 || 2049 < a) {
          throw "not proper year for UTCTime: " + a;
        }b = ("" + a).slice(-2);
      } else {
        b = ("000" + a).slice(-4);
      }b += ("0" + (g.getUTCMonth() + 1)).slice(-2);b += ("0" + g.getUTCDate()).slice(-2);b += ("0" + g.getUTCHours()).slice(-2);b += ("0" + g.getUTCMinutes()).slice(-2);b += ("0" + g.getUTCSeconds()).slice(-2);if (f) {
        var c = g.getUTCMilliseconds();if (c !== 0) {
          c = ("00" + c).slice(-3);c = c.replace(/0+$/g, "");b += "." + c;
        }
      }b += "Z";return b;
    }function uricmptohex(a) {
      return a.replace(/%/g, "");
    }function hextouricmp(a) {
      return a.replace(/(..)/g, "%$1");
    }function ipv6tohex(g) {
      var b = "malformed IPv6 address";if (!g.match(/^[0-9A-Fa-f:]+$/)) {
        throw b;
      }g = g.toLowerCase();var d = g.split(":").length - 1;if (d < 2) {
        throw b;
      }var e = ":".repeat(7 - d + 2);g = g.replace("::", e);var c = g.split(":");if (c.length != 8) {
        throw b;
      }for (var f = 0; f < 8; f++) {
        c[f] = ("0000" + c[f]).slice(-4);
      }return c.join("");
    }function hextoipv6(e) {
      if (!e.match(/^[0-9A-Fa-f]{32}$/)) {
        throw "malformed IPv6 address octet";
      }e = e.toLowerCase();var b = e.match(/.{1,4}/g);for (var d = 0; d < 8; d++) {
        b[d] = b[d].replace(/^0+/, "");if (b[d] == "") {
          b[d] = "0";
        }
      }e = ":" + b.join(":") + ":";var c = e.match(/:(0:){2,}/g);if (c === null) {
        return e.slice(1, -1);
      }var f = "";for (var d = 0; d < c.length; d++) {
        if (c[d].length > f.length) {
          f = c[d];
        }
      }e = e.replace(f, "::");return e.slice(1, -1);
    }function hextoip(b) {
      var d = "malformed hex value";if (!b.match(/^([0-9A-Fa-f][0-9A-Fa-f]){1,}$/)) {
        throw d;
      }if (b.length == 8) {
        var c;try {
          c = parseInt(b.substr(0, 2), 16) + "." + parseInt(b.substr(2, 2), 16) + "." + parseInt(b.substr(4, 2), 16) + "." + parseInt(b.substr(6, 2), 16);return c;
        } catch (a) {
          throw d;
        }
      } else {
        if (b.length == 32) {
          return hextoipv6(b);
        } else {
          return b;
        }
      }
    }function iptohex(f) {
      var j = "malformed IP address";f = f.toLowerCase(f);if (f.match(/^[0-9.]+$/)) {
        var b = f.split(".");if (b.length !== 4) {
          throw j;
        }var g = "";try {
          for (var e = 0; e < 4; e++) {
            var h = parseInt(b[e]);g += ("0" + h.toString(16)).slice(-2);
          }return g;
        } catch (c) {
          throw j;
        }
      } else {
        if (f.match(/^[0-9a-f:]+$/) && f.indexOf(":") !== -1) {
          return ipv6tohex(f);
        } else {
          throw j;
        }
      }
    }function encodeURIComponentAll(a) {
      var d = encodeURIComponent(a);var b = "";for (var c = 0; c < d.length; c++) {
        if (d[c] == "%") {
          b = b + d.substr(c, 3);c = c + 2;
        } else {
          b = b + "%" + stohex(d[c]);
        }
      }return b;
    }function newline_toUnix(a) {
      a = a.replace(/\r\n/mg, "\n");return a;
    }function newline_toDos(a) {
      a = a.replace(/\r\n/mg, "\n");a = a.replace(/\n/mg, "\r\n");return a;
    }KJUR.lang.String.isInteger = function (a) {
      if (a.match(/^[0-9]+$/)) {
        return true;
      } else {
        if (a.match(/^-[0-9]+$/)) {
          return true;
        } else {
          return false;
        }
      }
    };KJUR.lang.String.isHex = function (a) {
      if (a.length % 2 == 0 && (a.match(/^[0-9a-f]+$/) || a.match(/^[0-9A-F]+$/))) {
        return true;
      } else {
        return false;
      }
    };KJUR.lang.String.isBase64 = function (a) {
      a = a.replace(/\s+/g, "");if (a.match(/^[0-9A-Za-z+\/]+={0,3}$/) && a.length % 4 == 0) {
        return true;
      } else {
        return false;
      }
    };KJUR.lang.String.isBase64URL = function (a) {
      if (a.match(/[+/=]/)) {
        return false;
      }a = b64utob64(a);return KJUR.lang.String.isBase64(a);
    };KJUR.lang.String.isIntegerArray = function (a) {
      a = a.replace(/\s+/g, "");if (a.match(/^\[[0-9,]+\]$/)) {
        return true;
      } else {
        return false;
      }
    };function hextoposhex(a) {
      if (a.length % 2 == 1) {
        return "0" + a;
      }if (a.substr(0, 1) > "7") {
        return "00" + a;
      }return a;
    }function intarystrtohex(b) {
      b = b.replace(/^\s*\[\s*/, "");b = b.replace(/\s*\]\s*$/, "");b = b.replace(/\s*/g, "");try {
        var c = b.split(/,/).map(function (g, e, h) {
          var f = parseInt(g);if (f < 0 || 255 < f) {
            throw "integer not in range 0-255";
          }var d = ("00" + f.toString(16)).slice(-2);return d;
        }).join("");return c;
      } catch (a) {
        throw "malformed integer array string: " + a;
      }
    }var strdiffidx = function strdiffidx(c, a) {
      var d = c.length;if (c.length > a.length) {
        d = a.length;
      }for (var b = 0; b < d; b++) {
        if (c.charCodeAt(b) != a.charCodeAt(b)) {
          return b;
        }
      }if (c.length != a.length) {
        return d;
      }return -1;
    };
    if (typeof KJUR == "undefined" || !KJUR) {
      exports.KJUR = KJUR = {};
    }if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) {
      KJUR.crypto = {};
    }KJUR.crypto.Util = new function () {
      this.DIGESTINFOHEAD = { sha1: "3021300906052b0e03021a05000414", sha224: "302d300d06096086480165030402040500041c", sha256: "3031300d060960864801650304020105000420", sha384: "3041300d060960864801650304020205000430", sha512: "3051300d060960864801650304020305000440", md2: "3020300c06082a864886f70d020205000410", md5: "3020300c06082a864886f70d020505000410", ripemd160: "3021300906052b2403020105000414" };this.DEFAULTPROVIDER = { md5: "cryptojs", sha1: "cryptojs", sha224: "cryptojs", sha256: "cryptojs", sha384: "cryptojs", sha512: "cryptojs", ripemd160: "cryptojs", hmacmd5: "cryptojs", hmacsha1: "cryptojs", hmacsha224: "cryptojs", hmacsha256: "cryptojs", hmacsha384: "cryptojs", hmacsha512: "cryptojs", hmacripemd160: "cryptojs", MD5withRSA: "cryptojs/jsrsa", SHA1withRSA: "cryptojs/jsrsa", SHA224withRSA: "cryptojs/jsrsa", SHA256withRSA: "cryptojs/jsrsa", SHA384withRSA: "cryptojs/jsrsa", SHA512withRSA: "cryptojs/jsrsa", RIPEMD160withRSA: "cryptojs/jsrsa", MD5withECDSA: "cryptojs/jsrsa", SHA1withECDSA: "cryptojs/jsrsa", SHA224withECDSA: "cryptojs/jsrsa", SHA256withECDSA: "cryptojs/jsrsa", SHA384withECDSA: "cryptojs/jsrsa", SHA512withECDSA: "cryptojs/jsrsa", RIPEMD160withECDSA: "cryptojs/jsrsa", SHA1withDSA: "cryptojs/jsrsa", SHA224withDSA: "cryptojs/jsrsa", SHA256withDSA: "cryptojs/jsrsa", MD5withRSAandMGF1: "cryptojs/jsrsa", SHA1withRSAandMGF1: "cryptojs/jsrsa", SHA224withRSAandMGF1: "cryptojs/jsrsa", SHA256withRSAandMGF1: "cryptojs/jsrsa", SHA384withRSAandMGF1: "cryptojs/jsrsa", SHA512withRSAandMGF1: "cryptojs/jsrsa", RIPEMD160withRSAandMGF1: "cryptojs/jsrsa" };this.CRYPTOJSMESSAGEDIGESTNAME = { md5: CryptoJS.algo.MD5, sha1: CryptoJS.algo.SHA1, sha224: CryptoJS.algo.SHA224, sha256: CryptoJS.algo.SHA256, sha384: CryptoJS.algo.SHA384, sha512: CryptoJS.algo.SHA512, ripemd160: CryptoJS.algo.RIPEMD160 };this.getDigestInfoHex = function (a, b) {
        if (typeof this.DIGESTINFOHEAD[b] == "undefined") {
          throw "alg not supported in Util.DIGESTINFOHEAD: " + b;
        }return this.DIGESTINFOHEAD[b] + a;
      };this.getPaddedDigestInfoHex = function (h, a, j) {
        var c = this.getDigestInfoHex(h, a);var d = j / 4;if (c.length + 22 > d) {
          throw "key is too short for SigAlg: keylen=" + j + "," + a;
        }var b = "0001";var k = "00" + c;var g = "";var l = d - b.length - k.length;for (var f = 0; f < l; f += 2) {
          g += "ff";
        }var e = b + g + k;return e;
      };this.hashString = function (a, c) {
        var b = new KJUR.crypto.MessageDigest({ alg: c });return b.digestString(a);
      };this.hashHex = function (b, c) {
        var a = new KJUR.crypto.MessageDigest({ alg: c });return a.digestHex(b);
      };this.sha1 = function (a) {
        var b = new KJUR.crypto.MessageDigest({ alg: "sha1", prov: "cryptojs" });return b.digestString(a);
      };this.sha256 = function (a) {
        var b = new KJUR.crypto.MessageDigest({ alg: "sha256", prov: "cryptojs" });return b.digestString(a);
      };this.sha256Hex = function (a) {
        var b = new KJUR.crypto.MessageDigest({ alg: "sha256", prov: "cryptojs" });return b.digestHex(a);
      };this.sha512 = function (a) {
        var b = new KJUR.crypto.MessageDigest({ alg: "sha512", prov: "cryptojs" });return b.digestString(a);
      };this.sha512Hex = function (a) {
        var b = new KJUR.crypto.MessageDigest({ alg: "sha512", prov: "cryptojs" });return b.digestHex(a);
      };
    }();KJUR.crypto.Util.md5 = function (a) {
      var b = new KJUR.crypto.MessageDigest({ alg: "md5", prov: "cryptojs" });return b.digestString(a);
    };KJUR.crypto.Util.ripemd160 = function (a) {
      var b = new KJUR.crypto.MessageDigest({ alg: "ripemd160", prov: "cryptojs" });return b.digestString(a);
    };KJUR.crypto.Util.SECURERANDOMGEN = new SecureRandom();KJUR.crypto.Util.getRandomHexOfNbytes = function (b) {
      var a = new Array(b);KJUR.crypto.Util.SECURERANDOMGEN.nextBytes(a);return BAtohex(a);
    };KJUR.crypto.Util.getRandomBigIntegerOfNbytes = function (a) {
      return new BigInteger(KJUR.crypto.Util.getRandomHexOfNbytes(a), 16);
    };KJUR.crypto.Util.getRandomHexOfNbits = function (d) {
      var c = d % 8;var a = (d - c) / 8;var b = new Array(a + 1);KJUR.crypto.Util.SECURERANDOMGEN.nextBytes(b);b[0] = (255 << c & 255 ^ 255) & b[0];return BAtohex(b);
    };KJUR.crypto.Util.getRandomBigIntegerOfNbits = function (a) {
      return new BigInteger(KJUR.crypto.Util.getRandomHexOfNbits(a), 16);
    };KJUR.crypto.Util.getRandomBigIntegerZeroToMax = function (b) {
      var a = b.bitLength();while (1) {
        var c = KJUR.crypto.Util.getRandomBigIntegerOfNbits(a);if (b.compareTo(c) != -1) {
          return c;
        }
      }
    };KJUR.crypto.Util.getRandomBigIntegerMinToMax = function (e, b) {
      var c = e.compareTo(b);if (c == 1) {
        throw "biMin is greater than biMax";
      }if (c == 0) {
        return e;
      }var a = b.subtract(e);var d = KJUR.crypto.Util.getRandomBigIntegerZeroToMax(a);return d.add(e);
    };KJUR.crypto.MessageDigest = function (c) {
      var b = null;var a = null;var d = null;this.setAlgAndProvider = function (g, f) {
        g = KJUR.crypto.MessageDigest.getCanonicalAlgName(g);if (g !== null && f === undefined) {
          f = KJUR.crypto.Util.DEFAULTPROVIDER[g];
        }if (":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(g) != -1 && f == "cryptojs") {
          try {
            this.md = KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[g].create();
          } catch (e) {
            throw "setAlgAndProvider hash alg set fail alg=" + g + "/" + e;
          }this.updateString = function (h) {
            this.md.update(h);
          };this.updateHex = function (h) {
            var i = CryptoJS.enc.Hex.parse(h);this.md.update(i);
          };this.digest = function () {
            var h = this.md.finalize();return h.toString(CryptoJS.enc.Hex);
          };this.digestString = function (h) {
            this.updateString(h);return this.digest();
          };this.digestHex = function (h) {
            this.updateHex(h);return this.digest();
          };
        }if (":sha256:".indexOf(g) != -1 && f == "sjcl") {
          try {
            this.md = new sjcl.hash.sha256();
          } catch (e) {
            throw "setAlgAndProvider hash alg set fail alg=" + g + "/" + e;
          }this.updateString = function (h) {
            this.md.update(h);
          };this.updateHex = function (i) {
            var h = sjcl.codec.hex.toBits(i);this.md.update(h);
          };this.digest = function () {
            var h = this.md.finalize();return sjcl.codec.hex.fromBits(h);
          };this.digestString = function (h) {
            this.updateString(h);return this.digest();
          };this.digestHex = function (h) {
            this.updateHex(h);return this.digest();
          };
        }
      };this.updateString = function (e) {
        throw "updateString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName;
      };this.updateHex = function (e) {
        throw "updateHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName;
      };this.digest = function () {
        throw "digest() not supported for this alg/prov: " + this.algName + "/" + this.provName;
      };this.digestString = function (e) {
        throw "digestString(str) not supported for this alg/prov: " + this.algName + "/" + this.provName;
      };this.digestHex = function (e) {
        throw "digestHex(hex) not supported for this alg/prov: " + this.algName + "/" + this.provName;
      };if (c !== undefined) {
        if (c.alg !== undefined) {
          this.algName = c.alg;if (c.prov === undefined) {
            this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
          }this.setAlgAndProvider(this.algName, this.provName);
        }
      }
    };KJUR.crypto.MessageDigest.getCanonicalAlgName = function (a) {
      if (typeof a === "string") {
        a = a.toLowerCase();a = a.replace(/-/, "");
      }return a;
    };KJUR.crypto.MessageDigest.getHashLength = function (c) {
      var b = KJUR.crypto.MessageDigest;var a = b.getCanonicalAlgName(c);if (b.HASHLENGTH[a] === undefined) {
        throw "not supported algorithm: " + c;
      }return b.HASHLENGTH[a];
    };KJUR.crypto.MessageDigest.HASHLENGTH = { md5: 16, sha1: 20, sha224: 28, sha256: 32, sha384: 48, sha512: 64, ripemd160: 20 };KJUR.crypto.Mac = function (d) {
      var f = null;var c = null;var a = null;var e = null;var b = null;this.setAlgAndProvider = function (k, i) {
        k = k.toLowerCase();if (k == null) {
          k = "hmacsha1";
        }k = k.toLowerCase();if (k.substr(0, 4) != "hmac") {
          throw "setAlgAndProvider unsupported HMAC alg: " + k;
        }if (i === undefined) {
          i = KJUR.crypto.Util.DEFAULTPROVIDER[k];
        }this.algProv = k + "/" + i;var g = k.substr(4);if (":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(g) != -1 && i == "cryptojs") {
          try {
            var j = KJUR.crypto.Util.CRYPTOJSMESSAGEDIGESTNAME[g];this.mac = CryptoJS.algo.HMAC.create(j, this.pass);
          } catch (h) {
            throw "setAlgAndProvider hash alg set fail hashAlg=" + g + "/" + h;
          }this.updateString = function (l) {
            this.mac.update(l);
          };this.updateHex = function (l) {
            var m = CryptoJS.enc.Hex.parse(l);this.mac.update(m);
          };this.doFinal = function () {
            var l = this.mac.finalize();return l.toString(CryptoJS.enc.Hex);
          };this.doFinalString = function (l) {
            this.updateString(l);return this.doFinal();
          };this.doFinalHex = function (l) {
            this.updateHex(l);return this.doFinal();
          };
        }
      };this.updateString = function (g) {
        throw "updateString(str) not supported for this alg/prov: " + this.algProv;
      };this.updateHex = function (g) {
        throw "updateHex(hex) not supported for this alg/prov: " + this.algProv;
      };this.doFinal = function () {
        throw "digest() not supported for this alg/prov: " + this.algProv;
      };this.doFinalString = function (g) {
        throw "digestString(str) not supported for this alg/prov: " + this.algProv;
      };this.doFinalHex = function (g) {
        throw "digestHex(hex) not supported for this alg/prov: " + this.algProv;
      };this.setPassword = function (h) {
        if (typeof h == "string") {
          var g = h;if (h.length % 2 == 1 || !h.match(/^[0-9A-Fa-f]+$/)) {
            g = rstrtohex(h);
          }this.pass = CryptoJS.enc.Hex.parse(g);return;
        }if ((typeof h === "undefined" ? "undefined" : _typeof(h)) != "object") {
          throw "KJUR.crypto.Mac unsupported password type: " + h;
        }var g = null;if (h.hex !== undefined) {
          if (h.hex.length % 2 != 0 || !h.hex.match(/^[0-9A-Fa-f]+$/)) {
            throw "Mac: wrong hex password: " + h.hex;
          }g = h.hex;
        }if (h.utf8 !== undefined) {
          g = utf8tohex(h.utf8);
        }if (h.rstr !== undefined) {
          g = rstrtohex(h.rstr);
        }if (h.b64 !== undefined) {
          g = b64tohex(h.b64);
        }if (h.b64u !== undefined) {
          g = b64utohex(h.b64u);
        }if (g == null) {
          throw "KJUR.crypto.Mac unsupported password type: " + h;
        }this.pass = CryptoJS.enc.Hex.parse(g);
      };if (d !== undefined) {
        if (d.pass !== undefined) {
          this.setPassword(d.pass);
        }if (d.alg !== undefined) {
          this.algName = d.alg;if (d.prov === undefined) {
            this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
          }this.setAlgAndProvider(this.algName, this.provName);
        }
      }
    };KJUR.crypto.Signature = function (o) {
      var q = null;var n = null;var r = null;var c = null;var l = null;var d = null;var k = null;var h = null;var p = null;var e = null;var b = -1;var g = null;var j = null;var a = null;var i = null;var f = null;this._setAlgNames = function () {
        var s = this.algName.match(/^(.+)with(.+)$/);if (s) {
          this.mdAlgName = s[1].toLowerCase();this.pubkeyAlgName = s[2].toLowerCase();
        }
      };this._zeroPaddingOfSignature = function (x, w) {
        var v = "";var t = w / 4 - x.length;for (var u = 0; u < t; u++) {
          v = v + "0";
        }return v + x;
      };this.setAlgAndProvider = function (u, t) {
        this._setAlgNames();if (t != "cryptojs/jsrsa") {
          throw "provider not supported: " + t;
        }if (":md5:sha1:sha224:sha256:sha384:sha512:ripemd160:".indexOf(this.mdAlgName) != -1) {
          try {
            this.md = new KJUR.crypto.MessageDigest({ alg: this.mdAlgName });
          } catch (s) {
            throw "setAlgAndProvider hash alg set fail alg=" + this.mdAlgName + "/" + s;
          }this.init = function (w, x) {
            var y = null;try {
              if (x === undefined) {
                y = KEYUTIL.getKey(w);
              } else {
                y = KEYUTIL.getKey(w, x);
              }
            } catch (v) {
              throw "init failed:" + v;
            }if (y.isPrivate === true) {
              this.prvKey = y;this.state = "SIGN";
            } else {
              if (y.isPublic === true) {
                this.pubKey = y;this.state = "VERIFY";
              } else {
                throw "init failed.:" + y;
              }
            }
          };this.updateString = function (v) {
            this.md.updateString(v);
          };this.updateHex = function (v) {
            this.md.updateHex(v);
          };this.sign = function () {
            this.sHashHex = this.md.digest();if (typeof this.ecprvhex != "undefined" && typeof this.eccurvename != "undefined") {
              var v = new KJUR.crypto.ECDSA({ curve: this.eccurvename });this.hSign = v.signHex(this.sHashHex, this.ecprvhex);
            } else {
              if (this.prvKey instanceof RSAKey && this.pubkeyAlgName === "rsaandmgf1") {
                this.hSign = this.prvKey.signWithMessageHashPSS(this.sHashHex, this.mdAlgName, this.pssSaltLen);
              } else {
                if (this.prvKey instanceof RSAKey && this.pubkeyAlgName === "rsa") {
                  this.hSign = this.prvKey.signWithMessageHash(this.sHashHex, this.mdAlgName);
                } else {
                  if (this.prvKey instanceof KJUR.crypto.ECDSA) {
                    this.hSign = this.prvKey.signWithMessageHash(this.sHashHex);
                  } else {
                    if (this.prvKey instanceof KJUR.crypto.DSA) {
                      this.hSign = this.prvKey.signWithMessageHash(this.sHashHex);
                    } else {
                      throw "Signature: unsupported private key alg: " + this.pubkeyAlgName;
                    }
                  }
                }
              }
            }return this.hSign;
          };this.signString = function (v) {
            this.updateString(v);return this.sign();
          };this.signHex = function (v) {
            this.updateHex(v);return this.sign();
          };this.verify = function (v) {
            this.sHashHex = this.md.digest();if (typeof this.ecpubhex != "undefined" && typeof this.eccurvename != "undefined") {
              var w = new KJUR.crypto.ECDSA({ curve: this.eccurvename });return w.verifyHex(this.sHashHex, v, this.ecpubhex);
            } else {
              if (this.pubKey instanceof RSAKey && this.pubkeyAlgName === "rsaandmgf1") {
                return this.pubKey.verifyWithMessageHashPSS(this.sHashHex, v, this.mdAlgName, this.pssSaltLen);
              } else {
                if (this.pubKey instanceof RSAKey && this.pubkeyAlgName === "rsa") {
                  return this.pubKey.verifyWithMessageHash(this.sHashHex, v);
                } else {
                  if (KJUR.crypto.ECDSA !== undefined && this.pubKey instanceof KJUR.crypto.ECDSA) {
                    return this.pubKey.verifyWithMessageHash(this.sHashHex, v);
                  } else {
                    if (KJUR.crypto.DSA !== undefined && this.pubKey instanceof KJUR.crypto.DSA) {
                      return this.pubKey.verifyWithMessageHash(this.sHashHex, v);
                    } else {
                      throw "Signature: unsupported public key alg: " + this.pubkeyAlgName;
                    }
                  }
                }
              }
            }
          };
        }
      };this.init = function (s, t) {
        throw "init(key, pass) not supported for this alg:prov=" + this.algProvName;
      };this.updateString = function (s) {
        throw "updateString(str) not supported for this alg:prov=" + this.algProvName;
      };this.updateHex = function (s) {
        throw "updateHex(hex) not supported for this alg:prov=" + this.algProvName;
      };this.sign = function () {
        throw "sign() not supported for this alg:prov=" + this.algProvName;
      };this.signString = function (s) {
        throw "digestString(str) not supported for this alg:prov=" + this.algProvName;
      };this.signHex = function (s) {
        throw "digestHex(hex) not supported for this alg:prov=" + this.algProvName;
      };this.verify = function (s) {
        throw "verify(hSigVal) not supported for this alg:prov=" + this.algProvName;
      };this.initParams = o;if (o !== undefined) {
        if (o.alg !== undefined) {
          this.algName = o.alg;if (o.prov === undefined) {
            this.provName = KJUR.crypto.Util.DEFAULTPROVIDER[this.algName];
          } else {
            this.provName = o.prov;
          }this.algProvName = this.algName + ":" + this.provName;this.setAlgAndProvider(this.algName, this.provName);this._setAlgNames();
        }if (o.psssaltlen !== undefined) {
          this.pssSaltLen = o.psssaltlen;
        }if (o.prvkeypem !== undefined) {
          if (o.prvkeypas !== undefined) {
            throw "both prvkeypem and prvkeypas parameters not supported";
          } else {
            try {
              var q = KEYUTIL.getKey(o.prvkeypem);this.init(q);
            } catch (m) {
              throw "fatal error to load pem private key: " + m;
            }
          }
        }
      }
    };KJUR.crypto.Cipher = function (a) {};KJUR.crypto.Cipher.encrypt = function (e, f, d) {
      if (f instanceof RSAKey && f.isPublic) {
        var c = KJUR.crypto.Cipher.getAlgByKeyAndName(f, d);if (c === "RSA") {
          return f.encrypt(e);
        }if (c === "RSAOAEP") {
          return f.encryptOAEP(e, "sha1");
        }var b = c.match(/^RSAOAEP(\d+)$/);if (b !== null) {
          return f.encryptOAEP(e, "sha" + b[1]);
        }throw "Cipher.encrypt: unsupported algorithm for RSAKey: " + d;
      } else {
        throw "Cipher.encrypt: unsupported key or algorithm";
      }
    };KJUR.crypto.Cipher.decrypt = function (e, f, d) {
      if (f instanceof RSAKey && f.isPrivate) {
        var c = KJUR.crypto.Cipher.getAlgByKeyAndName(f, d);if (c === "RSA") {
          return f.decrypt(e);
        }if (c === "RSAOAEP") {
          return f.decryptOAEP(e, "sha1");
        }var b = c.match(/^RSAOAEP(\d+)$/);if (b !== null) {
          return f.decryptOAEP(e, "sha" + b[1]);
        }throw "Cipher.decrypt: unsupported algorithm for RSAKey: " + d;
      } else {
        throw "Cipher.decrypt: unsupported key or algorithm";
      }
    };KJUR.crypto.Cipher.getAlgByKeyAndName = function (b, a) {
      if (b instanceof RSAKey) {
        if (":RSA:RSAOAEP:RSAOAEP224:RSAOAEP256:RSAOAEP384:RSAOAEP512:".indexOf(a) != -1) {
          return a;
        }if (a === null || a === undefined) {
          return "RSA";
        }throw "getAlgByKeyAndName: not supported algorithm name for RSAKey: " + a;
      }throw "getAlgByKeyAndName: not supported algorithm name: " + a;
    };KJUR.crypto.OID = new function () {
      this.oidhex2name = { "2a864886f70d010101": "rsaEncryption", "2a8648ce3d0201": "ecPublicKey", "2a8648ce380401": "dsa", "2a8648ce3d030107": "secp256r1", "2b8104001f": "secp192k1", "2b81040021": "secp224r1", "2b8104000a": "secp256k1", "2b81040023": "secp521r1", "2b81040022": "secp384r1", "2a8648ce380403": "SHA1withDSA", "608648016503040301": "SHA224withDSA", "608648016503040302": "SHA256withDSA" };
    }();
    if (typeof KJUR == "undefined" || !KJUR) {
      exports.KJUR = KJUR = {};
    }if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) {
      KJUR.crypto = {};
    }KJUR.crypto.ECDSA = function (h) {
      var e = "secp256r1";var g = null;var b = null;var f = null;var a = new SecureRandom();var d = null;this.type = "EC";this.isPrivate = false;this.isPublic = false;function c(s, o, r, n) {
        var j = Math.max(o.bitLength(), n.bitLength());var t = s.add2D(r);var q = s.curve.getInfinity();for (var p = j - 1; p >= 0; --p) {
          q = q.twice2D();q.z = BigInteger.ONE;if (o.testBit(p)) {
            if (n.testBit(p)) {
              q = q.add2D(t);
            } else {
              q = q.add2D(s);
            }
          } else {
            if (n.testBit(p)) {
              q = q.add2D(r);
            }
          }
        }return q;
      }this.getBigRandom = function (i) {
        return new BigInteger(i.bitLength(), a).mod(i.subtract(BigInteger.ONE)).add(BigInteger.ONE);
      };this.setNamedCurve = function (i) {
        this.ecparams = KJUR.crypto.ECParameterDB.getByName(i);this.prvKeyHex = null;this.pubKeyHex = null;this.curveName = i;
      };this.setPrivateKeyHex = function (i) {
        this.isPrivate = true;this.prvKeyHex = i;
      };this.setPublicKeyHex = function (i) {
        this.isPublic = true;this.pubKeyHex = i;
      };this.getPublicKeyXYHex = function () {
        var k = this.pubKeyHex;if (k.substr(0, 2) !== "04") {
          throw "this method supports uncompressed format(04) only";
        }var j = this.ecparams.keylen / 4;if (k.length !== 2 + j * 2) {
          throw "malformed public key hex length";
        }var i = {};i.x = k.substr(2, j);i.y = k.substr(2 + j);return i;
      };this.getShortNISTPCurveName = function () {
        var i = this.curveName;if (i === "secp256r1" || i === "NIST P-256" || i === "P-256" || i === "prime256v1") {
          return "P-256";
        }if (i === "secp384r1" || i === "NIST P-384" || i === "P-384") {
          return "P-384";
        }return null;
      };this.generateKeyPairHex = function () {
        var k = this.ecparams.n;var n = this.getBigRandom(k);var l = this.ecparams.G.multiply(n);var q = l.getX().toBigInteger();var o = l.getY().toBigInteger();var i = this.ecparams.keylen / 4;var m = ("0000000000" + n.toString(16)).slice(-i);var r = ("0000000000" + q.toString(16)).slice(-i);var p = ("0000000000" + o.toString(16)).slice(-i);var j = "04" + r + p;this.setPrivateKeyHex(m);this.setPublicKeyHex(j);return { ecprvhex: m, ecpubhex: j };
      };this.signWithMessageHash = function (i) {
        return this.signHex(i, this.prvKeyHex);
      };this.signHex = function (o, j) {
        var t = new BigInteger(j, 16);var l = this.ecparams.n;var q = new BigInteger(o, 16);do {
          var m = this.getBigRandom(l);var u = this.ecparams.G;var p = u.multiply(m);var i = p.getX().toBigInteger().mod(l);
        } while (i.compareTo(BigInteger.ZERO) <= 0);var v = m.modInverse(l).multiply(q.add(t.multiply(i))).mod(l);return KJUR.crypto.ECDSA.biRSSigToASN1Sig(i, v);
      };this.sign = function (m, u) {
        var q = u;var j = this.ecparams.n;var p = BigInteger.fromByteArrayUnsigned(m);do {
          var l = this.getBigRandom(j);var t = this.ecparams.G;var o = t.multiply(l);var i = o.getX().toBigInteger().mod(j);
        } while (i.compareTo(BigInteger.ZERO) <= 0);var v = l.modInverse(j).multiply(p.add(q.multiply(i))).mod(j);return this.serializeSig(i, v);
      };this.verifyWithMessageHash = function (j, i) {
        return this.verifyHex(j, i, this.pubKeyHex);
      };this.verifyHex = function (m, i, p) {
        var l, j;var o = KJUR.crypto.ECDSA.parseSigHex(i);l = o.r;j = o.s;var k;k = ECPointFp.decodeFromHex(this.ecparams.curve, p);var n = new BigInteger(m, 16);return this.verifyRaw(n, l, j, k);
      };this.verify = function (o, p, j) {
        var l, i;if (Bitcoin.Util.isArray(p)) {
          var n = this.parseSig(p);l = n.r;i = n.s;
        } else {
          if ("object" === (typeof p === "undefined" ? "undefined" : _typeof(p)) && p.r && p.s) {
            l = p.r;i = p.s;
          } else {
            throw "Invalid value for signature";
          }
        }var k;if (j instanceof ECPointFp) {
          k = j;
        } else {
          if (Bitcoin.Util.isArray(j)) {
            k = ECPointFp.decodeFrom(this.ecparams.curve, j);
          } else {
            throw "Invalid format for pubkey value, must be byte array or ECPointFp";
          }
        }var m = BigInteger.fromByteArrayUnsigned(o);return this.verifyRaw(m, l, i, k);
      };this.verifyRaw = function (o, i, w, m) {
        var l = this.ecparams.n;var u = this.ecparams.G;if (i.compareTo(BigInteger.ONE) < 0 || i.compareTo(l) >= 0) {
          return false;
        }if (w.compareTo(BigInteger.ONE) < 0 || w.compareTo(l) >= 0) {
          return false;
        }var p = w.modInverse(l);var k = o.multiply(p).mod(l);var j = i.multiply(p).mod(l);var q = u.multiply(k).add(m.multiply(j));var t = q.getX().toBigInteger().mod(l);return t.equals(i);
      };this.serializeSig = function (k, j) {
        var l = k.toByteArraySigned();var i = j.toByteArraySigned();var m = [];m.push(2);m.push(l.length);m = m.concat(l);m.push(2);m.push(i.length);m = m.concat(i);m.unshift(m.length);m.unshift(48);return m;
      };this.parseSig = function (n) {
        var m;if (n[0] != 48) {
          throw new Error("Signature not a valid DERSequence");
        }m = 2;if (n[m] != 2) {
          throw new Error("First element in signature must be a DERInteger");
        }var l = n.slice(m + 2, m + 2 + n[m + 1]);m += 2 + n[m + 1];if (n[m] != 2) {
          throw new Error("Second element in signature must be a DERInteger");
        }var i = n.slice(m + 2, m + 2 + n[m + 1]);m += 2 + n[m + 1];var k = BigInteger.fromByteArrayUnsigned(l);var j = BigInteger.fromByteArrayUnsigned(i);return { r: k, s: j };
      };this.parseSigCompact = function (m) {
        if (m.length !== 65) {
          throw "Signature has the wrong length";
        }var j = m[0] - 27;if (j < 0 || j > 7) {
          throw "Invalid signature type";
        }var o = this.ecparams.n;var l = BigInteger.fromByteArrayUnsigned(m.slice(1, 33)).mod(o);var k = BigInteger.fromByteArrayUnsigned(m.slice(33, 65)).mod(o);return { r: l, s: k, i: j };
      };this.readPKCS5PrvKeyHex = function (l) {
        var n = ASN1HEX;var m = KJUR.crypto.ECDSA.getName;var p = n.getVbyList;if (n.isASN1HEX(l) === false) {
          throw "not ASN.1 hex string";
        }var i, k, o;try {
          i = p(l, 0, [2, 0], "06");k = p(l, 0, [1], "04");try {
            o = p(l, 0, [3, 0], "03").substr(2);
          } catch (j) {}
        } catch (j) {
          throw "malformed PKCS#1/5 plain ECC private key";
        }this.curveName = m(i);if (this.curveName === undefined) {
          throw "unsupported curve name";
        }this.setNamedCurve(this.curveName);this.setPublicKeyHex(o);this.setPrivateKeyHex(k);this.isPublic = false;
      };this.readPKCS8PrvKeyHex = function (l) {
        var q = ASN1HEX;var i = KJUR.crypto.ECDSA.getName;var n = q.getVbyList;if (q.isASN1HEX(l) === false) {
          throw "not ASN.1 hex string";
        }var j, p, m, k;try {
          j = n(l, 0, [1, 0], "06");p = n(l, 0, [1, 1], "06");m = n(l, 0, [2, 0, 1], "04");try {
            k = n(l, 0, [2, 0, 2, 0], "03").substr(2);
          } catch (o) {}
        } catch (o) {
          throw "malformed PKCS#8 plain ECC private key";
        }this.curveName = i(p);if (this.curveName === undefined) {
          throw "unsupported curve name";
        }this.setNamedCurve(this.curveName);this.setPublicKeyHex(k);this.setPrivateKeyHex(m);this.isPublic = false;
      };this.readPKCS8PubKeyHex = function (l) {
        var n = ASN1HEX;var m = KJUR.crypto.ECDSA.getName;var p = n.getVbyList;if (n.isASN1HEX(l) === false) {
          throw "not ASN.1 hex string";
        }var k, i, o;try {
          k = p(l, 0, [0, 0], "06");i = p(l, 0, [0, 1], "06");o = p(l, 0, [1], "03").substr(2);
        } catch (j) {
          throw "malformed PKCS#8 ECC public key";
        }this.curveName = m(i);if (this.curveName === null) {
          throw "unsupported curve name";
        }this.setNamedCurve(this.curveName);this.setPublicKeyHex(o);
      };this.readCertPubKeyHex = function (k, p) {
        if (p !== 5) {
          p = 6;
        }var m = ASN1HEX;var l = KJUR.crypto.ECDSA.getName;var o = m.getVbyList;if (m.isASN1HEX(k) === false) {
          throw "not ASN.1 hex string";
        }var i, n;try {
          i = o(k, 0, [0, p, 0, 1], "06");n = o(k, 0, [0, p, 1], "03").substr(2);
        } catch (j) {
          throw "malformed X.509 certificate ECC public key";
        }this.curveName = l(i);if (this.curveName === null) {
          throw "unsupported curve name";
        }this.setNamedCurve(this.curveName);this.setPublicKeyHex(n);
      };if (h !== undefined) {
        if (h.curve !== undefined) {
          this.curveName = h.curve;
        }
      }if (this.curveName === undefined) {
        this.curveName = e;
      }this.setNamedCurve(this.curveName);if (h !== undefined) {
        if (h.prv !== undefined) {
          this.setPrivateKeyHex(h.prv);
        }if (h.pub !== undefined) {
          this.setPublicKeyHex(h.pub);
        }
      }
    };KJUR.crypto.ECDSA.parseSigHex = function (a) {
      var b = KJUR.crypto.ECDSA.parseSigHexInHexRS(a);var d = new BigInteger(b.r, 16);var c = new BigInteger(b.s, 16);return { r: d, s: c };
    };KJUR.crypto.ECDSA.parseSigHexInHexRS = function (f) {
      var j = ASN1HEX;var i = j.getChildIdx;var g = j.getV;if (f.substr(0, 2) != "30") {
        throw "signature is not a ASN.1 sequence";
      }var h = i(f, 0);if (h.length != 2) {
        throw "number of signature ASN.1 sequence elements seem wrong";
      }var e = h[0];var d = h[1];if (f.substr(e, 2) != "02") {
        throw "1st item of sequene of signature is not ASN.1 integer";
      }if (f.substr(d, 2) != "02") {
        throw "2nd item of sequene of signature is not ASN.1 integer";
      }var c = g(f, e);var b = g(f, d);return { r: c, s: b };
    };KJUR.crypto.ECDSA.asn1SigToConcatSig = function (c) {
      var d = KJUR.crypto.ECDSA.parseSigHexInHexRS(c);var b = d.r;var a = d.s;if (b.substr(0, 2) == "00" && b.length % 32 == 2) {
        b = b.substr(2);
      }if (a.substr(0, 2) == "00" && a.length % 32 == 2) {
        a = a.substr(2);
      }if (b.length % 32 == 30) {
        b = "00" + b;
      }if (a.length % 32 == 30) {
        a = "00" + a;
      }if (b.length % 32 != 0) {
        throw "unknown ECDSA sig r length error";
      }if (a.length % 32 != 0) {
        throw "unknown ECDSA sig s length error";
      }return b + a;
    };KJUR.crypto.ECDSA.concatSigToASN1Sig = function (a) {
      if (a.length / 2 * 8 % (16 * 8) != 0) {
        throw "unknown ECDSA concatinated r-s sig  length error";
      }var c = a.substr(0, a.length / 2);var b = a.substr(a.length / 2);return KJUR.crypto.ECDSA.hexRSSigToASN1Sig(c, b);
    };KJUR.crypto.ECDSA.hexRSSigToASN1Sig = function (b, a) {
      var d = new BigInteger(b, 16);var c = new BigInteger(a, 16);return KJUR.crypto.ECDSA.biRSSigToASN1Sig(d, c);
    };KJUR.crypto.ECDSA.biRSSigToASN1Sig = function (f, d) {
      var c = KJUR.asn1;var b = new c.DERInteger({ bigint: f });var a = new c.DERInteger({ bigint: d });var e = new c.DERSequence({ array: [b, a] });return e.getEncodedHex();
    };KJUR.crypto.ECDSA.getName = function (a) {
      if (a === "2a8648ce3d030107") {
        return "secp256r1";
      }if (a === "2b8104000a") {
        return "secp256k1";
      }if (a === "2b81040022") {
        return "secp384r1";
      }if ("|secp256r1|NIST P-256|P-256|prime256v1|".indexOf(a) !== -1) {
        return "secp256r1";
      }if ("|secp256k1|".indexOf(a) !== -1) {
        return "secp256k1";
      }if ("|secp384r1|NIST P-384|P-384|".indexOf(a) !== -1) {
        return "secp384r1";
      }return null;
    };
    if (typeof KJUR == "undefined" || !KJUR) {
      exports.KJUR = KJUR = {};
    }if (typeof KJUR.crypto == "undefined" || !KJUR.crypto) {
      KJUR.crypto = {};
    }KJUR.crypto.ECParameterDB = new function () {
      var b = {};var c = {};function a(d) {
        return new BigInteger(d, 16);
      }this.getByName = function (e) {
        var d = e;if (typeof c[d] != "undefined") {
          d = c[e];
        }if (typeof b[d] != "undefined") {
          return b[d];
        }throw "unregistered EC curve name: " + d;
      };this.regist = function (A, l, o, g, m, e, j, f, k, u, d, x) {
        b[A] = {};var s = a(o);var z = a(g);var y = a(m);var t = a(e);var w = a(j);var r = new ECCurveFp(s, z, y);var q = r.decodePointHex("04" + f + k);b[A]["name"] = A;b[A]["keylen"] = l;b[A]["curve"] = r;b[A]["G"] = q;b[A]["n"] = t;b[A]["h"] = w;b[A]["oid"] = d;b[A]["info"] = x;for (var v = 0; v < u.length; v++) {
          c[u[v]] = A;
        }
      };
    }();KJUR.crypto.ECParameterDB.regist("secp128r1", 128, "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFDFFFFFFFFFFFFFFFFFFFFFFFC", "E87579C11079F43DD824993C2CEE5ED3", "FFFFFFFE0000000075A30D1B9038A115", "1", "161FF7528B899B2D0C28607CA52C5B86", "CF5AC8395BAFEB13C02DA292DDED7A83", [], "", "secp128r1 : SECG curve over a 128 bit prime field");KJUR.crypto.ECParameterDB.regist("secp160k1", 160, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFAC73", "0", "7", "0100000000000000000001B8FA16DFAB9ACA16B6B3", "1", "3B4C382CE37AA192A4019E763036F4F5DD4D7EBB", "938CF935318FDCED6BC28286531733C3F03C4FEE", [], "", "secp160k1 : SECG curve over a 160 bit prime field");KJUR.crypto.ECParameterDB.regist("secp160r1", 160, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF7FFFFFFC", "1C97BEFC54BD7A8B65ACF89F81D4D4ADC565FA45", "0100000000000000000001F4C8F927AED3CA752257", "1", "4A96B5688EF573284664698968C38BB913CBFC82", "23A628553168947D59DCC912042351377AC5FB32", [], "", "secp160r1 : SECG curve over a 160 bit prime field");KJUR.crypto.ECParameterDB.regist("secp192k1", 192, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFEE37", "0", "3", "FFFFFFFFFFFFFFFFFFFFFFFE26F2FC170F69466A74DEFD8D", "1", "DB4FF10EC057E9AE26B07D0280B7F4341DA5D1B1EAE06C7D", "9B2F2F6D9C5628A7844163D015BE86344082AA88D95E2F9D", []);KJUR.crypto.ECParameterDB.regist("secp192r1", 192, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFC", "64210519E59C80E70FA7E9AB72243049FEB8DEECC146B9B1", "FFFFFFFFFFFFFFFFFFFFFFFF99DEF836146BC9B1B4D22831", "1", "188DA80EB03090F67CBF20EB43A18800F4FF0AFD82FF1012", "07192B95FFC8DA78631011ED6B24CDD573F977A11E794811", []);KJUR.crypto.ECParameterDB.regist("secp224r1", 224, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF000000000000000000000001", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFE", "B4050A850C04B3ABF54132565044B0B7D7BFD8BA270B39432355FFB4", "FFFFFFFFFFFFFFFFFFFFFFFFFFFF16A2E0B8F03E13DD29455C5C2A3D", "1", "B70E0CBD6BB4BF7F321390B94A03C1D356C21122343280D6115C1D21", "BD376388B5F723FB4C22DFE6CD4375A05A07476444D5819985007E34", []);KJUR.crypto.ECParameterDB.regist("secp256k1", 256, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFC2F", "0", "7", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", "1", "79BE667EF9DCBBAC55A06295CE870B07029BFCDB2DCE28D959F2815B16F81798", "483ADA7726A3C4655DA4FBFC0E1108A8FD17B448A68554199C47D08FFB10D4B8", []);KJUR.crypto.ECParameterDB.regist("secp256r1", 256, "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFF", "FFFFFFFF00000001000000000000000000000000FFFFFFFFFFFFFFFFFFFFFFFC", "5AC635D8AA3A93E7B3EBBD55769886BC651D06B0CC53B0F63BCE3C3E27D2604B", "FFFFFFFF00000000FFFFFFFFFFFFFFFFBCE6FAADA7179E84F3B9CAC2FC632551", "1", "6B17D1F2E12C4247F8BCE6E563A440F277037D812DEB33A0F4A13945D898C296", "4FE342E2FE1A7F9B8EE7EB4A7C0F9E162BCE33576B315ECECBB6406837BF51F5", ["NIST P-256", "P-256", "prime256v1"]);KJUR.crypto.ECParameterDB.regist("secp384r1", 384, "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEFFFFFFFF0000000000000000FFFFFFFC", "B3312FA7E23EE7E4988E056BE3F82D19181D9C6EFE8141120314088F5013875AC656398D8A2ED19D2A85C8EDD3EC2AEF", "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC7634D81F4372DDF581A0DB248B0A77AECEC196ACCC52973", "1", "AA87CA22BE8B05378EB1C71EF320AD746E1D3B628BA79B9859F741E082542A385502F25DBF55296C3A545E3872760AB7", "3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5f", ["NIST P-384", "P-384"]);KJUR.crypto.ECParameterDB.regist("secp521r1", 521, "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF", "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFC", "051953EB9618E1C9A1F929A21A0B68540EEA2DA725B99B315F3B8B489918EF109E156193951EC7E937B1652C0BD3BB1BF073573DF883D2C34F1EF451FD46B503F00", "1FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFA51868783BF2F966B7FCC0148F709A5D03BB5C9B8899C47AEBB6FB71E91386409", "1", "C6858E06B70404E9CD9E3ECB662395B4429C648139053FB521F828AF606B4D3DBAA14B5E77EFE75928FE1DC127A2FFA8DE3348B3C1856A429BF97E7E31C2E5BD66", "011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650", ["NIST P-521", "P-521"]);
    var KEYUTIL = function () {
      var d = function d(p, r, q) {
        return k(CryptoJS.AES, p, r, q);
      };var e = function e(p, r, q) {
        return k(CryptoJS.TripleDES, p, r, q);
      };var a = function a(p, r, q) {
        return k(CryptoJS.DES, p, r, q);
      };var k = function k(s, x, u, q) {
        var r = CryptoJS.enc.Hex.parse(x);var w = CryptoJS.enc.Hex.parse(u);var p = CryptoJS.enc.Hex.parse(q);var t = {};t.key = w;t.iv = p;t.ciphertext = r;var v = s.decrypt(t, w, { iv: p });return CryptoJS.enc.Hex.stringify(v);
      };var l = function l(p, r, q) {
        return g(CryptoJS.AES, p, r, q);
      };var o = function o(p, r, q) {
        return g(CryptoJS.TripleDES, p, r, q);
      };var f = function f(p, r, q) {
        return g(CryptoJS.DES, p, r, q);
      };var g = function g(t, y, v, q) {
        var s = CryptoJS.enc.Hex.parse(y);var x = CryptoJS.enc.Hex.parse(v);var p = CryptoJS.enc.Hex.parse(q);var w = t.encrypt(s, x, { iv: p });var r = CryptoJS.enc.Hex.parse(w.toString());var u = CryptoJS.enc.Base64.stringify(r);return u;
      };var i = { "AES-256-CBC": { proc: d, eproc: l, keylen: 32, ivlen: 16 }, "AES-192-CBC": { proc: d, eproc: l, keylen: 24, ivlen: 16 }, "AES-128-CBC": { proc: d, eproc: l, keylen: 16, ivlen: 16 }, "DES-EDE3-CBC": { proc: e, eproc: o, keylen: 24, ivlen: 8 }, "DES-CBC": { proc: a, eproc: f, keylen: 8, ivlen: 8 } };var c = function c(p) {
        return i[p]["proc"];
      };var m = function m(p) {
        var r = CryptoJS.lib.WordArray.random(p);var q = CryptoJS.enc.Hex.stringify(r);return q;
      };var n = function n(v) {
        var w = {};var q = v.match(new RegExp("DEK-Info: ([^,]+),([0-9A-Fa-f]+)", "m"));if (q) {
          w.cipher = q[1];w.ivsalt = q[2];
        }var p = v.match(new RegExp("-----BEGIN ([A-Z]+) PRIVATE KEY-----"));if (p) {
          w.type = p[1];
        }var u = -1;var x = 0;if (v.indexOf("\r\n\r\n") != -1) {
          u = v.indexOf("\r\n\r\n");x = 2;
        }if (v.indexOf("\n\n") != -1) {
          u = v.indexOf("\n\n");x = 1;
        }var t = v.indexOf("-----END");if (u != -1 && t != -1) {
          var r = v.substring(u + x * 2, t - x);r = r.replace(/\s+/g, "");w.data = r;
        }return w;
      };var j = function j(q, y, p) {
        var v = p.substring(0, 16);var t = CryptoJS.enc.Hex.parse(v);var r = CryptoJS.enc.Utf8.parse(y);var u = i[q]["keylen"] + i[q]["ivlen"];var x = "";var w = null;for (;;) {
          var s = CryptoJS.algo.MD5.create();if (w != null) {
            s.update(w);
          }s.update(r);s.update(t);w = s.finalize();x = x + CryptoJS.enc.Hex.stringify(w);if (x.length >= u * 2) {
            break;
          }
        }var z = {};z.keyhex = x.substr(0, i[q]["keylen"] * 2);z.ivhex = x.substr(i[q]["keylen"] * 2, i[q]["ivlen"] * 2);return z;
      };var b = function b(p, v, r, w) {
        var s = CryptoJS.enc.Base64.parse(p);var q = CryptoJS.enc.Hex.stringify(s);var u = i[v]["proc"];var t = u(q, r, w);return t;
      };var h = function h(p, s, q, u) {
        var r = i[s]["eproc"];var t = r(p, q, u);return t;
      };return { version: "1.0.0", parsePKCS5PEM: function parsePKCS5PEM(p) {
          return n(p);
        }, getKeyAndUnusedIvByPasscodeAndIvsalt: function getKeyAndUnusedIvByPasscodeAndIvsalt(q, p, r) {
          return j(q, p, r);
        }, decryptKeyB64: function decryptKeyB64(p, r, q, s) {
          return b(p, r, q, s);
        }, getDecryptedKeyHex: function getDecryptedKeyHex(y, x) {
          var q = n(y);var t = q.type;var r = q.cipher;var p = q.ivsalt;var s = q.data;var w = j(r, x, p);var v = w.keyhex;var u = b(s, r, v, p);return u;
        }, getEncryptedPKCS5PEMFromPrvKeyHex: function getEncryptedPKCS5PEMFromPrvKeyHex(x, s, A, t, r) {
          var p = "";if (typeof t == "undefined" || t == null) {
            t = "AES-256-CBC";
          }if (typeof i[t] == "undefined") {
            throw "KEYUTIL unsupported algorithm: " + t;
          }if (typeof r == "undefined" || r == null) {
            var v = i[t]["ivlen"];var u = m(v);r = u.toUpperCase();
          }var z = j(t, A, r);var y = z.keyhex;var w = h(s, t, y, r);var q = w.replace(/(.{64})/g, "$1\r\n");var p = "-----BEGIN " + x + " PRIVATE KEY-----\r\n";p += "Proc-Type: 4,ENCRYPTED\r\n";p += "DEK-Info: " + t + "," + r + "\r\n";p += "\r\n";p += q;p += "\r\n-----END " + x + " PRIVATE KEY-----\r\n";return p;
        }, parseHexOfEncryptedPKCS8: function parseHexOfEncryptedPKCS8(y) {
          var B = ASN1HEX;var z = B.getChildIdx;var w = B.getV;var t = {};var r = z(y, 0);if (r.length != 2) {
            throw "malformed format: SEQUENCE(0).items != 2: " + r.length;
          }t.ciphertext = w(y, r[1]);var A = z(y, r[0]);if (A.length != 2) {
            throw "malformed format: SEQUENCE(0.0).items != 2: " + A.length;
          }if (w(y, A[0]) != "2a864886f70d01050d") {
            throw "this only supports pkcs5PBES2";
          }var p = z(y, A[1]);if (A.length != 2) {
            throw "malformed format: SEQUENCE(0.0.1).items != 2: " + p.length;
          }var q = z(y, p[1]);if (q.length != 2) {
            throw "malformed format: SEQUENCE(0.0.1.1).items != 2: " + q.length;
          }if (w(y, q[0]) != "2a864886f70d0307") {
            throw "this only supports TripleDES";
          }t.encryptionSchemeAlg = "TripleDES";t.encryptionSchemeIV = w(y, q[1]);var s = z(y, p[0]);if (s.length != 2) {
            throw "malformed format: SEQUENCE(0.0.1.0).items != 2: " + s.length;
          }if (w(y, s[0]) != "2a864886f70d01050c") {
            throw "this only supports pkcs5PBKDF2";
          }var x = z(y, s[1]);if (x.length < 2) {
            throw "malformed format: SEQUENCE(0.0.1.0.1).items < 2: " + x.length;
          }t.pbkdf2Salt = w(y, x[0]);var u = w(y, x[1]);try {
            t.pbkdf2Iter = parseInt(u, 16);
          } catch (v) {
            throw "malformed format pbkdf2Iter: " + u;
          }return t;
        }, getPBKDF2KeyHexFromParam: function getPBKDF2KeyHexFromParam(u, p) {
          var t = CryptoJS.enc.Hex.parse(u.pbkdf2Salt);var q = u.pbkdf2Iter;var s = CryptoJS.PBKDF2(p, t, { keySize: 192 / 32, iterations: q });var r = CryptoJS.enc.Hex.stringify(s);return r;
        }, _getPlainPKCS8HexFromEncryptedPKCS8PEM: function _getPlainPKCS8HexFromEncryptedPKCS8PEM(x, y) {
          var r = pemtohex(x, "ENCRYPTED PRIVATE KEY");var p = this.parseHexOfEncryptedPKCS8(r);var u = KEYUTIL.getPBKDF2KeyHexFromParam(p, y);var v = {};v.ciphertext = CryptoJS.enc.Hex.parse(p.ciphertext);var t = CryptoJS.enc.Hex.parse(u);var s = CryptoJS.enc.Hex.parse(p.encryptionSchemeIV);var w = CryptoJS.TripleDES.decrypt(v, t, { iv: s });var q = CryptoJS.enc.Hex.stringify(w);return q;
        }, getKeyFromEncryptedPKCS8PEM: function getKeyFromEncryptedPKCS8PEM(s, q) {
          var p = this._getPlainPKCS8HexFromEncryptedPKCS8PEM(s, q);var r = this.getKeyFromPlainPrivatePKCS8Hex(p);return r;
        }, parsePlainPrivatePKCS8Hex: function parsePlainPrivatePKCS8Hex(s) {
          var v = ASN1HEX;var u = v.getChildIdx;var t = v.getV;var q = {};q.algparam = null;if (s.substr(0, 2) != "30") {
            throw "malformed plain PKCS8 private key(code:001)";
          }var r = u(s, 0);if (r.length != 3) {
            throw "malformed plain PKCS8 private key(code:002)";
          }if (s.substr(r[1], 2) != "30") {
            throw "malformed PKCS8 private key(code:003)";
          }var p = u(s, r[1]);if (p.length != 2) {
            throw "malformed PKCS8 private key(code:004)";
          }if (s.substr(p[0], 2) != "06") {
            throw "malformed PKCS8 private key(code:005)";
          }q.algoid = t(s, p[0]);if (s.substr(p[1], 2) == "06") {
            q.algparam = t(s, p[1]);
          }if (s.substr(r[2], 2) != "04") {
            throw "malformed PKCS8 private key(code:006)";
          }q.keyidx = v.getVidx(s, r[2]);return q;
        }, getKeyFromPlainPrivatePKCS8PEM: function getKeyFromPlainPrivatePKCS8PEM(q) {
          var p = pemtohex(q, "PRIVATE KEY");var r = this.getKeyFromPlainPrivatePKCS8Hex(p);return r;
        }, getKeyFromPlainPrivatePKCS8Hex: function getKeyFromPlainPrivatePKCS8Hex(p) {
          var q = this.parsePlainPrivatePKCS8Hex(p);var r;if (q.algoid == "2a864886f70d010101") {
            r = new RSAKey();
          } else {
            if (q.algoid == "2a8648ce380401") {
              r = new KJUR.crypto.DSA();
            } else {
              if (q.algoid == "2a8648ce3d0201") {
                r = new KJUR.crypto.ECDSA();
              } else {
                throw "unsupported private key algorithm";
              }
            }
          }r.readPKCS8PrvKeyHex(p);return r;
        }, _getKeyFromPublicPKCS8Hex: function _getKeyFromPublicPKCS8Hex(q) {
          var p;var r = ASN1HEX.getVbyList(q, 0, [0, 0], "06");if (r === "2a864886f70d010101") {
            p = new RSAKey();
          } else {
            if (r === "2a8648ce380401") {
              p = new KJUR.crypto.DSA();
            } else {
              if (r === "2a8648ce3d0201") {
                p = new KJUR.crypto.ECDSA();
              } else {
                throw "unsupported PKCS#8 public key hex";
              }
            }
          }p.readPKCS8PubKeyHex(q);return p;
        }, parsePublicRawRSAKeyHex: function parsePublicRawRSAKeyHex(r) {
          var u = ASN1HEX;var t = u.getChildIdx;var s = u.getV;var p = {};if (r.substr(0, 2) != "30") {
            throw "malformed RSA key(code:001)";
          }var q = t(r, 0);if (q.length != 2) {
            throw "malformed RSA key(code:002)";
          }if (r.substr(q[0], 2) != "02") {
            throw "malformed RSA key(code:003)";
          }p.n = s(r, q[0]);if (r.substr(q[1], 2) != "02") {
            throw "malformed RSA key(code:004)";
          }p.e = s(r, q[1]);return p;
        }, parsePublicPKCS8Hex: function parsePublicPKCS8Hex(t) {
          var v = ASN1HEX;var u = v.getChildIdx;var s = v.getV;var q = {};q.algparam = null;var r = u(t, 0);if (r.length != 2) {
            throw "outer DERSequence shall have 2 elements: " + r.length;
          }var w = r[0];if (t.substr(w, 2) != "30") {
            throw "malformed PKCS8 public key(code:001)";
          }var p = u(t, w);if (p.length != 2) {
            throw "malformed PKCS8 public key(code:002)";
          }if (t.substr(p[0], 2) != "06") {
            throw "malformed PKCS8 public key(code:003)";
          }q.algoid = s(t, p[0]);if (t.substr(p[1], 2) == "06") {
            q.algparam = s(t, p[1]);
          } else {
            if (t.substr(p[1], 2) == "30") {
              q.algparam = {};q.algparam.p = v.getVbyList(t, p[1], [0], "02");q.algparam.q = v.getVbyList(t, p[1], [1], "02");q.algparam.g = v.getVbyList(t, p[1], [2], "02");
            }
          }if (t.substr(r[1], 2) != "03") {
            throw "malformed PKCS8 public key(code:004)";
          }q.key = s(t, r[1]).substr(2);return q;
        } };
    }();KEYUTIL.getKey = function (l, k, n) {
      var G = ASN1HEX,
          L = G.getChildIdx,
          v = G.getV,
          d = G.getVbyList,
          c = KJUR.crypto,
          i = c.ECDSA,
          C = c.DSA,
          w = RSAKey,
          M = pemtohex,
          F = KEYUTIL;if (typeof w != "undefined" && l instanceof w) {
        return l;
      }if (typeof i != "undefined" && l instanceof i) {
        return l;
      }if (typeof C != "undefined" && l instanceof C) {
        return l;
      }if (l.curve !== undefined && l.xy !== undefined && l.d === undefined) {
        return new i({ pub: l.xy, curve: l.curve });
      }if (l.curve !== undefined && l.d !== undefined) {
        return new i({ prv: l.d, curve: l.curve });
      }if (l.kty === undefined && l.n !== undefined && l.e !== undefined && l.d === undefined) {
        var P = new w();P.setPublic(l.n, l.e);return P;
      }if (l.kty === undefined && l.n !== undefined && l.e !== undefined && l.d !== undefined && l.p !== undefined && l.q !== undefined && l.dp !== undefined && l.dq !== undefined && l.co !== undefined && l.qi === undefined) {
        var P = new w();P.setPrivateEx(l.n, l.e, l.d, l.p, l.q, l.dp, l.dq, l.co);return P;
      }if (l.kty === undefined && l.n !== undefined && l.e !== undefined && l.d !== undefined && l.p === undefined) {
        var P = new w();P.setPrivate(l.n, l.e, l.d);return P;
      }if (l.p !== undefined && l.q !== undefined && l.g !== undefined && l.y !== undefined && l.x === undefined) {
        var P = new C();P.setPublic(l.p, l.q, l.g, l.y);return P;
      }if (l.p !== undefined && l.q !== undefined && l.g !== undefined && l.y !== undefined && l.x !== undefined) {
        var P = new C();P.setPrivate(l.p, l.q, l.g, l.y, l.x);return P;
      }if (l.kty === "RSA" && l.n !== undefined && l.e !== undefined && l.d === undefined) {
        var P = new w();P.setPublic(b64utohex(l.n), b64utohex(l.e));return P;
      }if (l.kty === "RSA" && l.n !== undefined && l.e !== undefined && l.d !== undefined && l.p !== undefined && l.q !== undefined && l.dp !== undefined && l.dq !== undefined && l.qi !== undefined) {
        var P = new w();P.setPrivateEx(b64utohex(l.n), b64utohex(l.e), b64utohex(l.d), b64utohex(l.p), b64utohex(l.q), b64utohex(l.dp), b64utohex(l.dq), b64utohex(l.qi));return P;
      }if (l.kty === "RSA" && l.n !== undefined && l.e !== undefined && l.d !== undefined) {
        var P = new w();P.setPrivate(b64utohex(l.n), b64utohex(l.e), b64utohex(l.d));return P;
      }if (l.kty === "EC" && l.crv !== undefined && l.x !== undefined && l.y !== undefined && l.d === undefined) {
        var j = new i({ curve: l.crv });var t = j.ecparams.keylen / 4;var B = ("0000000000" + b64utohex(l.x)).slice(-t);var z = ("0000000000" + b64utohex(l.y)).slice(-t);var u = "04" + B + z;j.setPublicKeyHex(u);return j;
      }if (l.kty === "EC" && l.crv !== undefined && l.x !== undefined && l.y !== undefined && l.d !== undefined) {
        var j = new i({ curve: l.crv });var t = j.ecparams.keylen / 4;var B = ("0000000000" + b64utohex(l.x)).slice(-t);var z = ("0000000000" + b64utohex(l.y)).slice(-t);var u = "04" + B + z;var b = ("0000000000" + b64utohex(l.d)).slice(-t);j.setPublicKeyHex(u);j.setPrivateKeyHex(b);return j;
      }if (n === "pkcs5prv") {
        var J = l,
            G = ASN1HEX,
            N,
            P;N = L(J, 0);if (N.length === 9) {
          P = new w();P.readPKCS5PrvKeyHex(J);
        } else {
          if (N.length === 6) {
            P = new C();P.readPKCS5PrvKeyHex(J);
          } else {
            if (N.length > 2 && J.substr(N[1], 2) === "04") {
              P = new i();P.readPKCS5PrvKeyHex(J);
            } else {
              throw "unsupported PKCS#1/5 hexadecimal key";
            }
          }
        }return P;
      }if (n === "pkcs8prv") {
        var P = F.getKeyFromPlainPrivatePKCS8Hex(l);return P;
      }if (n === "pkcs8pub") {
        return F._getKeyFromPublicPKCS8Hex(l);
      }if (n === "x509pub") {
        return X509.getPublicKeyFromCertHex(l);
      }if (l.indexOf("-END CERTIFICATE-", 0) != -1 || l.indexOf("-END X509 CERTIFICATE-", 0) != -1 || l.indexOf("-END TRUSTED CERTIFICATE-", 0) != -1) {
        return X509.getPublicKeyFromCertPEM(l);
      }if (l.indexOf("-END PUBLIC KEY-") != -1) {
        var O = pemtohex(l, "PUBLIC KEY");return F._getKeyFromPublicPKCS8Hex(O);
      }if (l.indexOf("-END RSA PRIVATE KEY-") != -1 && l.indexOf("4,ENCRYPTED") == -1) {
        var m = M(l, "RSA PRIVATE KEY");return F.getKey(m, null, "pkcs5prv");
      }if (l.indexOf("-END DSA PRIVATE KEY-") != -1 && l.indexOf("4,ENCRYPTED") == -1) {
        var I = M(l, "DSA PRIVATE KEY");var E = d(I, 0, [1], "02");var D = d(I, 0, [2], "02");var K = d(I, 0, [3], "02");var r = d(I, 0, [4], "02");var s = d(I, 0, [5], "02");var P = new C();P.setPrivate(new BigInteger(E, 16), new BigInteger(D, 16), new BigInteger(K, 16), new BigInteger(r, 16), new BigInteger(s, 16));return P;
      }if (l.indexOf("-END PRIVATE KEY-") != -1) {
        return F.getKeyFromPlainPrivatePKCS8PEM(l);
      }if (l.indexOf("-END RSA PRIVATE KEY-") != -1 && l.indexOf("4,ENCRYPTED") != -1) {
        var o = F.getDecryptedKeyHex(l, k);var H = new RSAKey();H.readPKCS5PrvKeyHex(o);return H;
      }if (l.indexOf("-END EC PRIVATE KEY-") != -1 && l.indexOf("4,ENCRYPTED") != -1) {
        var I = F.getDecryptedKeyHex(l, k);var P = d(I, 0, [1], "04");var f = d(I, 0, [2, 0], "06");var A = d(I, 0, [3, 0], "03").substr(2);var e = "";if (KJUR.crypto.OID.oidhex2name[f] !== undefined) {
          e = KJUR.crypto.OID.oidhex2name[f];
        } else {
          throw "undefined OID(hex) in KJUR.crypto.OID: " + f;
        }var j = new i({ curve: e });j.setPublicKeyHex(A);j.setPrivateKeyHex(P);j.isPublic = false;return j;
      }if (l.indexOf("-END DSA PRIVATE KEY-") != -1 && l.indexOf("4,ENCRYPTED") != -1) {
        var I = F.getDecryptedKeyHex(l, k);var E = d(I, 0, [1], "02");var D = d(I, 0, [2], "02");var K = d(I, 0, [3], "02");var r = d(I, 0, [4], "02");var s = d(I, 0, [5], "02");var P = new C();P.setPrivate(new BigInteger(E, 16), new BigInteger(D, 16), new BigInteger(K, 16), new BigInteger(r, 16), new BigInteger(s, 16));return P;
      }if (l.indexOf("-END ENCRYPTED PRIVATE KEY-") != -1) {
        return F.getKeyFromEncryptedPKCS8PEM(l, k);
      }throw "not supported argument";
    };KEYUTIL.generateKeypair = function (a, c) {
      if (a == "RSA") {
        var b = c;var h = new RSAKey();h.generate(b, "10001");h.isPrivate = true;h.isPublic = true;var f = new RSAKey();var e = h.n.toString(16);var i = h.e.toString(16);f.setPublic(e, i);f.isPrivate = false;f.isPublic = true;var k = {};k.prvKeyObj = h;k.pubKeyObj = f;return k;
      } else {
        if (a == "EC") {
          var d = c;var g = new KJUR.crypto.ECDSA({ curve: d });var j = g.generateKeyPairHex();var h = new KJUR.crypto.ECDSA({ curve: d });h.setPublicKeyHex(j.ecpubhex);h.setPrivateKeyHex(j.ecprvhex);h.isPrivate = true;h.isPublic = false;var f = new KJUR.crypto.ECDSA({ curve: d });f.setPublicKeyHex(j.ecpubhex);f.isPrivate = false;f.isPublic = true;var k = {};k.prvKeyObj = h;k.pubKeyObj = f;return k;
        } else {
          throw "unknown algorithm: " + a;
        }
      }
    };KEYUTIL.getPEM = function (b, D, y, m, q, j) {
      var F = KJUR,
          k = F.asn1,
          z = k.DERObjectIdentifier,
          f = k.DERInteger,
          l = k.ASN1Util.newObject,
          a = k.x509,
          C = a.SubjectPublicKeyInfo,
          e = F.crypto,
          u = e.DSA,
          r = e.ECDSA,
          n = RSAKey;function A(s) {
        var G = l({ seq: [{ "int": 0 }, { "int": { bigint: s.n } }, { "int": s.e }, { "int": { bigint: s.d } }, { "int": { bigint: s.p } }, { "int": { bigint: s.q } }, { "int": { bigint: s.dmp1 } }, { "int": { bigint: s.dmq1 } }, { "int": { bigint: s.coeff } }] });return G;
      }function B(G) {
        var s = l({ seq: [{ "int": 1 }, { octstr: { hex: G.prvKeyHex } }, { tag: ["a0", true, { oid: { name: G.curveName } }] }, { tag: ["a1", true, { bitstr: { hex: "00" + G.pubKeyHex } }] }] });return s;
      }function x(s) {
        var G = l({ seq: [{ "int": 0 }, { "int": { bigint: s.p } }, { "int": { bigint: s.q } }, { "int": { bigint: s.g } }, { "int": { bigint: s.y } }, { "int": { bigint: s.x } }] });return G;
      }if ((n !== undefined && b instanceof n || u !== undefined && b instanceof u || r !== undefined && b instanceof r) && b.isPublic == true && (D === undefined || D == "PKCS8PUB")) {
        var E = new C(b);var w = E.getEncodedHex();return hextopem(w, "PUBLIC KEY");
      }if (D == "PKCS1PRV" && n !== undefined && b instanceof n && (y === undefined || y == null) && b.isPrivate == true) {
        var E = A(b);var w = E.getEncodedHex();return hextopem(w, "RSA PRIVATE KEY");
      }if (D == "PKCS1PRV" && r !== undefined && b instanceof r && (y === undefined || y == null) && b.isPrivate == true) {
        var i = new z({ name: b.curveName });var v = i.getEncodedHex();var h = B(b);var t = h.getEncodedHex();var p = "";p += hextopem(v, "EC PARAMETERS");p += hextopem(t, "EC PRIVATE KEY");return p;
      }if (D == "PKCS1PRV" && u !== undefined && b instanceof u && (y === undefined || y == null) && b.isPrivate == true) {
        var E = x(b);var w = E.getEncodedHex();return hextopem(w, "DSA PRIVATE KEY");
      }if (D == "PKCS5PRV" && n !== undefined && b instanceof n && y !== undefined && y != null && b.isPrivate == true) {
        var E = A(b);var w = E.getEncodedHex();if (m === undefined) {
          m = "DES-EDE3-CBC";
        }return this.getEncryptedPKCS5PEMFromPrvKeyHex("RSA", w, y, m, j);
      }if (D == "PKCS5PRV" && r !== undefined && b instanceof r && y !== undefined && y != null && b.isPrivate == true) {
        var E = B(b);var w = E.getEncodedHex();if (m === undefined) {
          m = "DES-EDE3-CBC";
        }return this.getEncryptedPKCS5PEMFromPrvKeyHex("EC", w, y, m, j);
      }if (D == "PKCS5PRV" && u !== undefined && b instanceof u && y !== undefined && y != null && b.isPrivate == true) {
        var E = x(b);var w = E.getEncodedHex();if (m === undefined) {
          m = "DES-EDE3-CBC";
        }return this.getEncryptedPKCS5PEMFromPrvKeyHex("DSA", w, y, m, j);
      }var o = function o(G, s) {
        var I = c(G, s);var H = new l({ seq: [{ seq: [{ oid: { name: "pkcs5PBES2" } }, { seq: [{ seq: [{ oid: { name: "pkcs5PBKDF2" } }, { seq: [{ octstr: { hex: I.pbkdf2Salt } }, { "int": I.pbkdf2Iter }] }] }, { seq: [{ oid: { name: "des-EDE3-CBC" } }, { octstr: { hex: I.encryptionSchemeIV } }] }] }] }, { octstr: { hex: I.ciphertext } }] });return H.getEncodedHex();
      };var c = function c(N, O) {
        var H = 100;var M = CryptoJS.lib.WordArray.random(8);var L = "DES-EDE3-CBC";var s = CryptoJS.lib.WordArray.random(8);var I = CryptoJS.PBKDF2(O, M, { keySize: 192 / 32, iterations: H });var J = CryptoJS.enc.Hex.parse(N);var K = CryptoJS.TripleDES.encrypt(J, I, { iv: s }) + "";var G = {};G.ciphertext = K;G.pbkdf2Salt = CryptoJS.enc.Hex.stringify(M);G.pbkdf2Iter = H;G.encryptionSchemeAlg = L;G.encryptionSchemeIV = CryptoJS.enc.Hex.stringify(s);return G;
      };if (D == "PKCS8PRV" && n != undefined && b instanceof n && b.isPrivate == true) {
        var g = A(b);var d = g.getEncodedHex();var E = l({ seq: [{ "int": 0 }, { seq: [{ oid: { name: "rsaEncryption" } }, { "null": true }] }, { octstr: { hex: d } }] });var w = E.getEncodedHex();if (y === undefined || y == null) {
          return hextopem(w, "PRIVATE KEY");
        } else {
          var t = o(w, y);return hextopem(t, "ENCRYPTED PRIVATE KEY");
        }
      }if (D == "PKCS8PRV" && r !== undefined && b instanceof r && b.isPrivate == true) {
        var g = new l({ seq: [{ "int": 1 }, { octstr: { hex: b.prvKeyHex } }, { tag: ["a1", true, { bitstr: { hex: "00" + b.pubKeyHex } }] }] });var d = g.getEncodedHex();var E = l({ seq: [{ "int": 0 }, { seq: [{ oid: { name: "ecPublicKey" } }, { oid: { name: b.curveName } }] }, { octstr: { hex: d } }] });var w = E.getEncodedHex();if (y === undefined || y == null) {
          return hextopem(w, "PRIVATE KEY");
        } else {
          var t = o(w, y);return hextopem(t, "ENCRYPTED PRIVATE KEY");
        }
      }if (D == "PKCS8PRV" && u !== undefined && b instanceof u && b.isPrivate == true) {
        var g = new f({ bigint: b.x });var d = g.getEncodedHex();var E = l({ seq: [{ "int": 0 }, { seq: [{ oid: { name: "dsa" } }, { seq: [{ "int": { bigint: b.p } }, { "int": { bigint: b.q } }, { "int": { bigint: b.g } }] }] }, { octstr: { hex: d } }] });var w = E.getEncodedHex();if (y === undefined || y == null) {
          return hextopem(w, "PRIVATE KEY");
        } else {
          var t = o(w, y);return hextopem(t, "ENCRYPTED PRIVATE KEY");
        }
      }throw "unsupported object nor format";
    };KEYUTIL.getKeyFromCSRPEM = function (b) {
      var a = pemtohex(b, "CERTIFICATE REQUEST");var c = KEYUTIL.getKeyFromCSRHex(a);return c;
    };KEYUTIL.getKeyFromCSRHex = function (a) {
      var c = KEYUTIL.parseCSRHex(a);var b = KEYUTIL.getKey(c.p8pubkeyhex, null, "pkcs8pub");return b;
    };KEYUTIL.parseCSRHex = function (d) {
      var i = ASN1HEX;var f = i.getChildIdx;var c = i.getTLV;var b = {};var g = d;if (g.substr(0, 2) != "30") {
        throw "malformed CSR(code:001)";
      }var e = f(g, 0);if (e.length < 1) {
        throw "malformed CSR(code:002)";
      }if (g.substr(e[0], 2) != "30") {
        throw "malformed CSR(code:003)";
      }var a = f(g, e[0]);if (a.length < 3) {
        throw "malformed CSR(code:004)";
      }b.p8pubkeyhex = c(g, a[2]);return b;
    };KEYUTIL.getJWKFromKey = function (d) {
      var b = {};if (d instanceof RSAKey && d.isPrivate) {
        b.kty = "RSA";b.n = hextob64u(d.n.toString(16));b.e = hextob64u(d.e.toString(16));b.d = hextob64u(d.d.toString(16));b.p = hextob64u(d.p.toString(16));b.q = hextob64u(d.q.toString(16));b.dp = hextob64u(d.dmp1.toString(16));b.dq = hextob64u(d.dmq1.toString(16));b.qi = hextob64u(d.coeff.toString(16));return b;
      } else {
        if (d instanceof RSAKey && d.isPublic) {
          b.kty = "RSA";b.n = hextob64u(d.n.toString(16));b.e = hextob64u(d.e.toString(16));return b;
        } else {
          if (d instanceof KJUR.crypto.ECDSA && d.isPrivate) {
            var a = d.getShortNISTPCurveName();if (a !== "P-256" && a !== "P-384") {
              throw "unsupported curve name for JWT: " + a;
            }var c = d.getPublicKeyXYHex();b.kty = "EC";b.crv = a;b.x = hextob64u(c.x);b.y = hextob64u(c.y);b.d = hextob64u(d.prvKeyHex);return b;
          } else {
            if (d instanceof KJUR.crypto.ECDSA && d.isPublic) {
              var a = d.getShortNISTPCurveName();if (a !== "P-256" && a !== "P-384") {
                throw "unsupported curve name for JWT: " + a;
              }var c = d.getPublicKeyXYHex();b.kty = "EC";b.crv = a;b.x = hextob64u(c.x);b.y = hextob64u(c.y);return b;
            }
          }
        }
      }throw "not supported key object";
    };
    RSAKey.getPosArrayOfChildrenFromHex = function (a) {
      return ASN1HEX.getChildIdx(a, 0);
    };RSAKey.getHexValueArrayOfChildrenFromHex = function (f) {
      var n = ASN1HEX;var i = n.getV;var k = RSAKey.getPosArrayOfChildrenFromHex(f);var e = i(f, k[0]);var j = i(f, k[1]);var b = i(f, k[2]);var c = i(f, k[3]);var h = i(f, k[4]);var g = i(f, k[5]);var m = i(f, k[6]);var l = i(f, k[7]);var d = i(f, k[8]);var k = new Array();k.push(e, j, b, c, h, g, m, l, d);return k;
    };RSAKey.prototype.readPrivateKeyFromPEMString = function (d) {
      var c = pemtohex(d);var b = RSAKey.getHexValueArrayOfChildrenFromHex(c);this.setPrivateEx(b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]);
    };RSAKey.prototype.readPKCS5PrvKeyHex = function (c) {
      var b = RSAKey.getHexValueArrayOfChildrenFromHex(c);this.setPrivateEx(b[1], b[2], b[3], b[4], b[5], b[6], b[7], b[8]);
    };RSAKey.prototype.readPKCS8PrvKeyHex = function (e) {
      var c, j, l, b, a, f, d, k;var m = ASN1HEX;var g = m.getVbyList;if (m.isASN1HEX(e) === false) {
        throw "not ASN.1 hex string";
      }try {
        c = g(e, 0, [2, 0, 1], "02");j = g(e, 0, [2, 0, 2], "02");l = g(e, 0, [2, 0, 3], "02");b = g(e, 0, [2, 0, 4], "02");a = g(e, 0, [2, 0, 5], "02");f = g(e, 0, [2, 0, 6], "02");d = g(e, 0, [2, 0, 7], "02");k = g(e, 0, [2, 0, 8], "02");
      } catch (i) {
        throw "malformed PKCS#8 plain RSA private key";
      }this.setPrivateEx(c, j, l, b, a, f, d, k);
    };RSAKey.prototype.readPKCS5PubKeyHex = function (c) {
      var e = ASN1HEX;var b = e.getV;if (e.isASN1HEX(c) === false) {
        throw "keyHex is not ASN.1 hex string";
      }var a = e.getChildIdx(c, 0);if (a.length !== 2 || c.substr(a[0], 2) !== "02" || c.substr(a[1], 2) !== "02") {
        throw "wrong hex for PKCS#5 public key";
      }var f = b(c, a[0]);var d = b(c, a[1]);this.setPublic(f, d);
    };RSAKey.prototype.readPKCS8PubKeyHex = function (b) {
      var c = ASN1HEX;if (c.isASN1HEX(b) === false) {
        throw "not ASN.1 hex string";
      }if (c.getTLVbyList(b, 0, [0, 0]) !== "06092a864886f70d010101") {
        throw "not PKCS8 RSA public key";
      }var a = c.getTLVbyList(b, 0, [1, 0]);this.readPKCS5PubKeyHex(a);
    };RSAKey.prototype.readCertPubKeyHex = function (b, d) {
      var a, c;a = new X509();a.readCertHex(b);c = a.getPublicKeyHex();this.readPKCS8PubKeyHex(c);
    };
    var _RE_HEXDECONLY = new RegExp("");_RE_HEXDECONLY.compile("[^0-9a-f]", "gi");function _rsasign_getHexPaddedDigestInfoForString(d, e, a) {
      var b = function b(f) {
        return KJUR.crypto.Util.hashString(f, a);
      };var c = b(d);return KJUR.crypto.Util.getPaddedDigestInfoHex(c, a, e);
    }function _zeroPaddingOfSignature(e, d) {
      var c = "";var a = d / 4 - e.length;for (var b = 0; b < a; b++) {
        c = c + "0";
      }return c + e;
    }RSAKey.prototype.sign = function (d, a) {
      var b = function b(e) {
        return KJUR.crypto.Util.hashString(e, a);
      };var c = b(d);return this.signWithMessageHash(c, a);
    };RSAKey.prototype.signWithMessageHash = function (e, c) {
      var f = KJUR.crypto.Util.getPaddedDigestInfoHex(e, c, this.n.bitLength());var b = parseBigInt(f, 16);var d = this.doPrivate(b);var a = d.toString(16);return _zeroPaddingOfSignature(a, this.n.bitLength());
    };function pss_mgf1_str(c, a, e) {
      var b = "",
          d = 0;while (b.length < a) {
        b += hextorstr(e(rstrtohex(c + String.fromCharCode.apply(String, [(d & 4278190080) >> 24, (d & 16711680) >> 16, (d & 65280) >> 8, d & 255]))));d += 1;
      }return b;
    }RSAKey.prototype.signPSS = function (e, a, d) {
      var c = function c(f) {
        return KJUR.crypto.Util.hashHex(f, a);
      };var b = c(rstrtohex(e));if (d === undefined) {
        d = -1;
      }return this.signWithMessageHashPSS(b, a, d);
    };RSAKey.prototype.signWithMessageHashPSS = function (l, a, k) {
      var b = hextorstr(l);var g = b.length;var m = this.n.bitLength() - 1;var c = Math.ceil(m / 8);var d;var o = function o(i) {
        return KJUR.crypto.Util.hashHex(i, a);
      };if (k === -1 || k === undefined) {
        k = g;
      } else {
        if (k === -2) {
          k = c - g - 2;
        } else {
          if (k < -2) {
            throw "invalid salt length";
          }
        }
      }if (c < g + k + 2) {
        throw "data too long";
      }var f = "";if (k > 0) {
        f = new Array(k);new SecureRandom().nextBytes(f);f = String.fromCharCode.apply(String, f);
      }var n = hextorstr(o(rstrtohex("\x00\x00\x00\x00\x00\x00\x00\x00" + b + f)));var j = [];for (d = 0; d < c - k - g - 2; d += 1) {
        j[d] = 0;
      }var e = String.fromCharCode.apply(String, j) + "\x01" + f;var h = pss_mgf1_str(n, e.length, o);var q = [];for (d = 0; d < e.length; d += 1) {
        q[d] = e.charCodeAt(d) ^ h.charCodeAt(d);
      }var p = 65280 >> 8 * c - m & 255;q[0] &= ~p;for (d = 0; d < g; d++) {
        q.push(n.charCodeAt(d));
      }q.push(188);return _zeroPaddingOfSignature(this.doPrivate(new BigInteger(q)).toString(16), this.n.bitLength());
    };function _rsasign_getDecryptSignatureBI(a, d, c) {
      var b = new RSAKey();b.setPublic(d, c);var e = b.doPublic(a);return e;
    }function _rsasign_getHexDigestInfoFromSig(a, c, b) {
      var e = _rsasign_getDecryptSignatureBI(a, c, b);var d = e.toString(16).replace(/^1f+00/, "");return d;
    }function _rsasign_getAlgNameAndHashFromHexDisgestInfo(f) {
      for (var e in KJUR.crypto.Util.DIGESTINFOHEAD) {
        var d = KJUR.crypto.Util.DIGESTINFOHEAD[e];var b = d.length;if (f.substring(0, b) == d) {
          var c = [e, f.substring(b)];return c;
        }
      }return [];
    }RSAKey.prototype.verify = function (f, j) {
      j = j.replace(_RE_HEXDECONLY, "");j = j.replace(/[ \n]+/g, "");var b = parseBigInt(j, 16);if (b.bitLength() > this.n.bitLength()) {
        return 0;
      }var i = this.doPublic(b);var e = i.toString(16).replace(/^1f+00/, "");var g = _rsasign_getAlgNameAndHashFromHexDisgestInfo(e);if (g.length == 0) {
        return false;
      }var d = g[0];var h = g[1];var a = function a(k) {
        return KJUR.crypto.Util.hashString(k, d);
      };var c = a(f);return h == c;
    };RSAKey.prototype.verifyWithMessageHash = function (e, a) {
      a = a.replace(_RE_HEXDECONLY, "");a = a.replace(/[ \n]+/g, "");var b = parseBigInt(a, 16);if (b.bitLength() > this.n.bitLength()) {
        return 0;
      }var h = this.doPublic(b);var g = h.toString(16).replace(/^1f+00/, "");var c = _rsasign_getAlgNameAndHashFromHexDisgestInfo(g);if (c.length == 0) {
        return false;
      }var d = c[0];var f = c[1];return f == e;
    };RSAKey.prototype.verifyPSS = function (c, b, a, f) {
      var e = function e(g) {
        return KJUR.crypto.Util.hashHex(g, a);
      };var d = e(rstrtohex(c));if (f === undefined) {
        f = -1;
      }return this.verifyWithMessageHashPSS(d, b, a, f);
    };RSAKey.prototype.verifyWithMessageHashPSS = function (f, s, l, c) {
      var k = new BigInteger(s, 16);if (k.bitLength() > this.n.bitLength()) {
        return false;
      }var r = function r(i) {
        return KJUR.crypto.Util.hashHex(i, l);
      };var j = hextorstr(f);var h = j.length;var g = this.n.bitLength() - 1;var m = Math.ceil(g / 8);var q;if (c === -1 || c === undefined) {
        c = h;
      } else {
        if (c === -2) {
          c = m - h - 2;
        } else {
          if (c < -2) {
            throw "invalid salt length";
          }
        }
      }if (m < h + c + 2) {
        throw "data too long";
      }var a = this.doPublic(k).toByteArray();for (q = 0; q < a.length; q += 1) {
        a[q] &= 255;
      }while (a.length < m) {
        a.unshift(0);
      }if (a[m - 1] !== 188) {
        throw "encoded message does not end in 0xbc";
      }a = String.fromCharCode.apply(String, a);var d = a.substr(0, m - h - 1);var e = a.substr(d.length, h);var p = 65280 >> 8 * m - g & 255;if ((d.charCodeAt(0) & p) !== 0) {
        throw "bits beyond keysize not zero";
      }var n = pss_mgf1_str(e, d.length, r);var o = [];for (q = 0; q < d.length; q += 1) {
        o[q] = d.charCodeAt(q) ^ n.charCodeAt(q);
      }o[0] &= ~p;var b = m - h - c - 2;for (q = 0; q < b; q += 1) {
        if (o[q] !== 0) {
          throw "leftmost octets not zero";
        }
      }if (o[b] !== 1) {
        throw "0x01 marker not found";
      }return e === hextorstr(r(rstrtohex("\x00\x00\x00\x00\x00\x00\x00\x00" + j + String.fromCharCode.apply(String, o.slice(-c)))));
    };RSAKey.SALT_LEN_HLEN = -1;RSAKey.SALT_LEN_MAX = -2;RSAKey.SALT_LEN_RECOVER = -2;
    function X509() {
      var k = ASN1HEX,
          j = k.getChildIdx,
          h = k.getV,
          b = k.getTLV,
          f = k.getVbyList,
          c = k.getTLVbyList,
          g = k.getIdxbyList,
          d = k.getVidx,
          i = k.oidname,
          a = X509,
          e = pemtohex;this.hex = null;this.version = 0;this.foffset = 0;this.aExtInfo = null;this.getVersion = function () {
        if (this.hex === null || this.version !== 0) {
          return this.version;
        }if (c(this.hex, 0, [0, 0]) !== "a003020102") {
          this.version = 1;this.foffset = -1;return 1;
        }this.version = 3;return 3;
      };this.getSerialNumberHex = function () {
        return f(this.hex, 0, [0, 1 + this.foffset], "02");
      };this.getSignatureAlgorithmField = function () {
        return i(f(this.hex, 0, [0, 2 + this.foffset, 0], "06"));
      };this.getIssuerHex = function () {
        return c(this.hex, 0, [0, 3 + this.foffset], "30");
      };this.getIssuerString = function () {
        return a.hex2dn(this.getIssuerHex());
      };this.getSubjectHex = function () {
        return c(this.hex, 0, [0, 5 + this.foffset], "30");
      };this.getSubjectString = function () {
        return a.hex2dn(this.getSubjectHex());
      };this.getNotBefore = function () {
        var l = f(this.hex, 0, [0, 4 + this.foffset, 0]);l = l.replace(/(..)/g, "%$1");l = decodeURIComponent(l);return l;
      };this.getNotAfter = function () {
        var l = f(this.hex, 0, [0, 4 + this.foffset, 1]);l = l.replace(/(..)/g, "%$1");l = decodeURIComponent(l);return l;
      };this.getPublicKeyHex = function () {
        return k.getTLVbyList(this.hex, 0, [0, 6 + this.foffset], "30");
      };this.getPublicKeyIdx = function () {
        return g(this.hex, 0, [0, 6 + this.foffset], "30");
      };this.getPublicKeyContentIdx = function () {
        var l = this.getPublicKeyIdx();return g(this.hex, l, [1, 0], "30");
      };this.getPublicKey = function () {
        return KEYUTIL.getKey(this.getPublicKeyHex(), null, "pkcs8pub");
      };this.getSignatureAlgorithmName = function () {
        return i(f(this.hex, 0, [1, 0], "06"));
      };this.getSignatureValueHex = function () {
        return f(this.hex, 0, [2], "03", true);
      };this.verifySignature = function (n) {
        var o = this.getSignatureAlgorithmName();var l = this.getSignatureValueHex();var m = c(this.hex, 0, [0], "30");var p = new KJUR.crypto.Signature({ alg: o });p.init(n);p.updateHex(m);return p.verify(l);
      };this.parseExt = function () {
        if (this.version !== 3) {
          return -1;
        }var p = g(this.hex, 0, [0, 7, 0], "30");var m = j(this.hex, p);this.aExtInfo = new Array();for (var n = 0; n < m.length; n++) {
          var q = {};q.critical = false;var l = j(this.hex, m[n]);var r = 0;if (l.length === 3) {
            q.critical = true;r = 1;
          }q.oid = k.hextooidstr(f(this.hex, m[n], [0], "06"));var o = g(this.hex, m[n], [1 + r]);q.vidx = d(this.hex, o);this.aExtInfo.push(q);
        }
      };this.getExtInfo = function (n) {
        var l = this.aExtInfo;var o = n;if (!n.match(/^[0-9.]+$/)) {
          o = KJUR.asn1.x509.OID.name2oid(n);
        }if (o === "") {
          return undefined;
        }for (var m = 0; m < l.length; m++) {
          if (l[m].oid === o) {
            return l[m];
          }
        }return undefined;
      };this.getExtBasicConstraints = function () {
        var n = this.getExtInfo("basicConstraints");if (n === undefined) {
          return n;
        }var l = h(this.hex, n.vidx);if (l === "") {
          return {};
        }if (l === "0101ff") {
          return { cA: true };
        }if (l.substr(0, 8) === "0101ff02") {
          var o = h(l, 6);var m = parseInt(o, 16);return { cA: true, pathLen: m };
        }throw "basicConstraints parse error";
      };this.getExtKeyUsageBin = function () {
        var o = this.getExtInfo("keyUsage");if (o === undefined) {
          return "";
        }var m = h(this.hex, o.vidx);if (m.length % 2 != 0 || m.length <= 2) {
          throw "malformed key usage value";
        }var l = parseInt(m.substr(0, 2));var n = parseInt(m.substr(2), 16).toString(2);return n.substr(0, n.length - l);
      };this.getExtKeyUsageString = function () {
        var n = this.getExtKeyUsageBin();var l = new Array();for (var m = 0; m < n.length; m++) {
          if (n.substr(m, 1) == "1") {
            l.push(X509.KEYUSAGE_NAME[m]);
          }
        }return l.join(",");
      };this.getExtSubjectKeyIdentifier = function () {
        var l = this.getExtInfo("subjectKeyIdentifier");if (l === undefined) {
          return l;
        }return h(this.hex, l.vidx);
      };this.getExtAuthorityKeyIdentifier = function () {
        var p = this.getExtInfo("authorityKeyIdentifier");if (p === undefined) {
          return p;
        }var l = {};var o = b(this.hex, p.vidx);var m = j(o, 0);for (var n = 0; n < m.length; n++) {
          if (o.substr(m[n], 2) === "80") {
            l.kid = h(o, m[n]);
          }
        }return l;
      };this.getExtExtKeyUsageName = function () {
        var p = this.getExtInfo("extKeyUsage");if (p === undefined) {
          return p;
        }var l = new Array();var o = b(this.hex, p.vidx);if (o === "") {
          return l;
        }var m = j(o, 0);for (var n = 0; n < m.length; n++) {
          l.push(i(h(o, m[n])));
        }return l;
      };this.getExtSubjectAltName = function () {
        var m = this.getExtSubjectAltName2();var l = new Array();for (var n = 0; n < m.length; n++) {
          if (m[n][0] === "DNS") {
            l.push(m[n][1]);
          }
        }return l;
      };this.getExtSubjectAltName2 = function () {
        var p, s, r;var q = this.getExtInfo("subjectAltName");if (q === undefined) {
          return q;
        }var l = new Array();var o = b(this.hex, q.vidx);var m = j(o, 0);for (var n = 0; n < m.length; n++) {
          r = o.substr(m[n], 2);p = h(o, m[n]);if (r === "81") {
            s = hextoutf8(p);l.push(["MAIL", s]);
          }if (r === "82") {
            s = hextoutf8(p);l.push(["DNS", s]);
          }if (r === "84") {
            s = X509.hex2dn(p, 0);l.push(["DN", s]);
          }if (r === "86") {
            s = hextoutf8(p);l.push(["URI", s]);
          }if (r === "87") {
            s = hextoip(p);l.push(["IP", s]);
          }
        }return l;
      };this.getExtCRLDistributionPointsURI = function () {
        var q = this.getExtInfo("cRLDistributionPoints");if (q === undefined) {
          return q;
        }var l = new Array();var m = j(this.hex, q.vidx);for (var o = 0; o < m.length; o++) {
          try {
            var r = f(this.hex, m[o], [0, 0, 0], "86");var p = hextoutf8(r);l.push(p);
          } catch (n) {}
        }return l;
      };this.getExtAIAInfo = function () {
        var p = this.getExtInfo("authorityInfoAccess");if (p === undefined) {
          return p;
        }var l = { ocsp: [], caissuer: [] };var m = j(this.hex, p.vidx);for (var n = 0; n < m.length; n++) {
          var q = f(this.hex, m[n], [0], "06");var o = f(this.hex, m[n], [1], "86");if (q === "2b06010505073001") {
            l.ocsp.push(hextoutf8(o));
          }if (q === "2b06010505073002") {
            l.caissuer.push(hextoutf8(o));
          }
        }return l;
      };this.getExtCertificatePolicies = function () {
        var o = this.getExtInfo("certificatePolicies");if (o === undefined) {
          return o;
        }var l = b(this.hex, o.vidx);var u = [];var s = j(l, 0);for (var r = 0; r < s.length; r++) {
          var t = {};var n = j(l, s[r]);t.id = i(h(l, n[0]));if (n.length === 2) {
            var m = j(l, n[1]);for (var q = 0; q < m.length; q++) {
              var p = f(l, m[q], [0], "06");if (p === "2b06010505070201") {
                t.cps = hextoutf8(f(l, m[q], [1]));
              } else {
                if (p === "2b06010505070202") {
                  t.unotice = hextoutf8(f(l, m[q], [1, 0]));
                }
              }
            }
          }u.push(t);
        }return u;
      };this.readCertPEM = function (l) {
        this.readCertHex(e(l));
      };this.readCertHex = function (l) {
        this.hex = l;this.getVersion();try {
          g(this.hex, 0, [0, 7], "a3");this.parseExt();
        } catch (m) {}
      };this.getInfo = function () {
        var m = X509;var B, u, z;B = "Basic Fields\n";B += "  serial number: " + this.getSerialNumberHex() + "\n";B += "  signature algorithm: " + this.getSignatureAlgorithmField() + "\n";B += "  issuer: " + this.getIssuerString() + "\n";B += "  notBefore: " + this.getNotBefore() + "\n";B += "  notAfter: " + this.getNotAfter() + "\n";B += "  subject: " + this.getSubjectString() + "\n";B += "  subject public key info: \n";u = this.getPublicKey();B += "    key algorithm: " + u.type + "\n";if (u.type === "RSA") {
          B += "    n=" + hextoposhex(u.n.toString(16)).substr(0, 16) + "...\n";B += "    e=" + hextoposhex(u.e.toString(16)) + "\n";
        }z = this.aExtInfo;if (z !== undefined && z !== null) {
          B += "X509v3 Extensions:\n";for (var r = 0; r < z.length; r++) {
            var n = z[r];var A = KJUR.asn1.x509.OID.oid2name(n.oid);if (A === "") {
              A = n.oid;
            }var x = "";if (n.critical === true) {
              x = "CRITICAL";
            }B += "  " + A + " " + x + ":\n";if (A === "basicConstraints") {
              var v = this.getExtBasicConstraints();if (v.cA === undefined) {
                B += "    {}\n";
              } else {
                B += "    cA=true";if (v.pathLen !== undefined) {
                  B += ", pathLen=" + v.pathLen;
                }B += "\n";
              }
            } else {
              if (A === "keyUsage") {
                B += "    " + this.getExtKeyUsageString() + "\n";
              } else {
                if (A === "subjectKeyIdentifier") {
                  B += "    " + this.getExtSubjectKeyIdentifier() + "\n";
                } else {
                  if (A === "authorityKeyIdentifier") {
                    var l = this.getExtAuthorityKeyIdentifier();if (l.kid !== undefined) {
                      B += "    kid=" + l.kid + "\n";
                    }
                  } else {
                    if (A === "extKeyUsage") {
                      var w = this.getExtExtKeyUsageName();B += "    " + w.join(", ") + "\n";
                    } else {
                      if (A === "subjectAltName") {
                        var t = this.getExtSubjectAltName2();B += "    " + t + "\n";
                      } else {
                        if (A === "cRLDistributionPoints") {
                          var y = this.getExtCRLDistributionPointsURI();B += "    " + y + "\n";
                        } else {
                          if (A === "authorityInfoAccess") {
                            var p = this.getExtAIAInfo();if (p.ocsp !== undefined) {
                              B += "    ocsp: " + p.ocsp.join(",") + "\n";
                            }if (p.caissuer !== undefined) {
                              B += "    caissuer: " + p.caissuer.join(",") + "\n";
                            }
                          } else {
                            if (A === "certificatePolicies") {
                              var o = this.getExtCertificatePolicies();for (var q = 0; q < o.length; q++) {
                                if (o[q].id !== undefined) {
                                  B += "    policy oid: " + o[q].id + "\n";
                                }if (o[q].cps !== undefined) {
                                  B += "    cps: " + o[q].cps + "\n";
                                }
                              }
                            }
                          }
                        }
                      }
                    }
                  }
                }
              }
            }
          }
        }B += "signature algorithm: " + this.getSignatureAlgorithmName() + "\n";B += "signature: " + this.getSignatureValueHex().substr(0, 16) + "...\n";return B;
      };
    }X509.hex2dn = function (f, b) {
      if (b === undefined) {
        b = 0;
      }if (f.substr(b, 2) !== "30") {
        throw "malformed DN";
      }var c = new Array();var d = ASN1HEX.getChildIdx(f, b);for (var e = 0; e < d.length; e++) {
        c.push(X509.hex2rdn(f, d[e]));
      }c = c.map(function (a) {
        return a.replace("/", "\\/");
      });return "/" + c.join("/");
    };X509.hex2rdn = function (f, b) {
      if (b === undefined) {
        b = 0;
      }if (f.substr(b, 2) !== "31") {
        throw "malformed RDN";
      }var c = new Array();var d = ASN1HEX.getChildIdx(f, b);for (var e = 0; e < d.length; e++) {
        c.push(X509.hex2attrTypeValue(f, d[e]));
      }c = c.map(function (a) {
        return a.replace("+", "\\+");
      });return c.join("+");
    };X509.hex2attrTypeValue = function (d, i) {
      var j = ASN1HEX;var h = j.getV;if (i === undefined) {
        i = 0;
      }if (d.substr(i, 2) !== "30") {
        throw "malformed attribute type and value";
      }var g = j.getChildIdx(d, i);if (g.length !== 2 || d.substr(g[0], 2) !== "06") {
        "malformed attribute type and value";
      }var b = h(d, g[0]);var f = KJUR.asn1.ASN1Util.oidHexToInt(b);var e = KJUR.asn1.x509.OID.oid2atype(f);var a = h(d, g[1]);var c = hextorstr(a);return e + "=" + c;
    };X509.getPublicKeyFromCertHex = function (b) {
      var a = new X509();a.readCertHex(b);return a.getPublicKey();
    };X509.getPublicKeyFromCertPEM = function (b) {
      var a = new X509();a.readCertPEM(b);return a.getPublicKey();
    };X509.getPublicKeyInfoPropOfCertPEM = function (c) {
      var e = ASN1HEX;var g = e.getVbyList;var b = {};var a, f, d;b.algparam = null;a = new X509();a.readCertPEM(c);f = a.getPublicKeyHex();b.keyhex = g(f, 0, [1], "03").substr(2);b.algoid = g(f, 0, [0, 0], "06");if (b.algoid === "2a8648ce3d0201") {
        b.algparam = g(f, 0, [0, 1], "06");
      }return b;
    };X509.KEYUSAGE_NAME = ["digitalSignature", "nonRepudiation", "keyEncipherment", "dataEncipherment", "keyAgreement", "keyCertSign", "cRLSign", "encipherOnly", "decipherOnly"];
    if (typeof KJUR == "undefined" || !KJUR) {
      exports.KJUR = KJUR = {};
    }if (typeof KJUR.jws == "undefined" || !KJUR.jws) {
      KJUR.jws = {};
    }KJUR.jws.JWS = function () {
      var b = KJUR,
          a = b.jws.JWS,
          c = a.isSafeJSONString;this.parseJWS = function (g, j) {
        if (this.parsedJWS !== undefined && (j || this.parsedJWS.sigvalH !== undefined)) {
          return;
        }var i = g.match(/^([^.]+)\.([^.]+)\.([^.]+)$/);if (i == null) {
          throw "JWS signature is not a form of 'Head.Payload.SigValue'.";
        }var k = i[1];var e = i[2];var l = i[3];var n = k + "." + e;this.parsedJWS = {};this.parsedJWS.headB64U = k;this.parsedJWS.payloadB64U = e;this.parsedJWS.sigvalB64U = l;this.parsedJWS.si = n;if (!j) {
          var h = b64utohex(l);var f = parseBigInt(h, 16);this.parsedJWS.sigvalH = h;this.parsedJWS.sigvalBI = f;
        }var d = b64utoutf8(k);var m = b64utoutf8(e);this.parsedJWS.headS = d;this.parsedJWS.payloadS = m;if (!c(d, this.parsedJWS, "headP")) {
          throw "malformed JSON string for JWS Head: " + d;
        }
      };
    };KJUR.jws.JWS.sign = function (i, v, y, z, a) {
      var w = KJUR,
          m = w.jws,
          q = m.JWS,
          g = q.readSafeJSONString,
          p = q.isSafeJSONString,
          d = w.crypto,
          k = d.ECDSA,
          o = d.Mac,
          c = d.Signature,
          t = JSON;var s, j, n;if (typeof v != "string" && (typeof v === "undefined" ? "undefined" : _typeof(v)) != "object") {
        throw "spHeader must be JSON string or object: " + v;
      }if ((typeof v === "undefined" ? "undefined" : _typeof(v)) == "object") {
        j = v;s = t.stringify(j);
      }if (typeof v == "string") {
        s = v;if (!p(s)) {
          throw "JWS Head is not safe JSON string: " + s;
        }j = g(s);
      }n = y;if ((typeof y === "undefined" ? "undefined" : _typeof(y)) == "object") {
        n = t.stringify(y);
      }if ((i == "" || i == null) && j.alg !== undefined) {
        i = j.alg;
      }if (i != "" && i != null && j.alg === undefined) {
        j.alg = i;s = t.stringify(j);
      }if (i !== j.alg) {
        throw "alg and sHeader.alg doesn't match: " + i + "!=" + j.alg;
      }var r = null;if (q.jwsalg2sigalg[i] === undefined) {
        throw "unsupported alg name: " + i;
      } else {
        r = q.jwsalg2sigalg[i];
      }var e = utf8tob64u(s);var l = utf8tob64u(n);var b = e + "." + l;var x = "";if (r.substr(0, 4) == "Hmac") {
        if (z === undefined) {
          throw "mac key shall be specified for HS* alg";
        }var h = new o({ alg: r, prov: "cryptojs", pass: z });h.updateString(b);x = h.doFinal();
      } else {
        if (r.indexOf("withECDSA") != -1) {
          var f = new c({ alg: r });f.init(z, a);f.updateString(b);hASN1Sig = f.sign();x = KJUR.crypto.ECDSA.asn1SigToConcatSig(hASN1Sig);
        } else {
          if (r != "none") {
            var f = new c({ alg: r });f.init(z, a);f.updateString(b);x = f.sign();
          }
        }
      }var u = hextob64u(x);return b + "." + u;
    };KJUR.jws.JWS.verify = function (w, B, n) {
      var x = KJUR,
          q = x.jws,
          t = q.JWS,
          i = t.readSafeJSONString,
          e = x.crypto,
          p = e.ECDSA,
          s = e.Mac,
          d = e.Signature,
          m;if ((typeof RSAKey === "undefined" ? "undefined" : _typeof(RSAKey)) !== undefined) {
        m = RSAKey;
      }var y = w.split(".");if (y.length !== 3) {
        return false;
      }var f = y[0];var r = y[1];var c = f + "." + r;var A = b64utohex(y[2]);var l = i(b64utoutf8(y[0]));var k = null;var z = null;if (l.alg === undefined) {
        throw "algorithm not specified in header";
      } else {
        k = l.alg;z = k.substr(0, 2);
      }if (n != null && Object.prototype.toString.call(n) === "[object Array]" && n.length > 0) {
        var b = ":" + n.join(":") + ":";if (b.indexOf(":" + k + ":") == -1) {
          throw "algorithm '" + k + "' not accepted in the list";
        }
      }if (k != "none" && B === null) {
        throw "key shall be specified to verify.";
      }if (typeof B == "string" && B.indexOf("-----BEGIN ") != -1) {
        B = KEYUTIL.getKey(B);
      }if (z == "RS" || z == "PS") {
        if (!(B instanceof m)) {
          throw "key shall be a RSAKey obj for RS* and PS* algs";
        }
      }if (z == "ES") {
        if (!(B instanceof p)) {
          throw "key shall be a ECDSA obj for ES* algs";
        }
      }if (k == "none") {}var u = null;if (t.jwsalg2sigalg[l.alg] === undefined) {
        throw "unsupported alg name: " + k;
      } else {
        u = t.jwsalg2sigalg[k];
      }if (u == "none") {
        throw "not supported";
      } else {
        if (u.substr(0, 4) == "Hmac") {
          var o = null;if (B === undefined) {
            throw "hexadecimal key shall be specified for HMAC";
          }var j = new s({ alg: u, pass: B });j.updateString(c);o = j.doFinal();return A == o;
        } else {
          if (u.indexOf("withECDSA") != -1) {
            var h = null;try {
              h = p.concatSigToASN1Sig(A);
            } catch (v) {
              return false;
            }var g = new d({ alg: u });g.init(B);g.updateString(c);return g.verify(h);
          } else {
            var g = new d({ alg: u });g.init(B);g.updateString(c);return g.verify(A);
          }
        }
      }
    };KJUR.jws.JWS.parse = function (g) {
      var c = g.split(".");var b = {};var f, e, d;if (c.length != 2 && c.length != 3) {
        throw "malformed sJWS: wrong number of '.' splitted elements";
      }f = c[0];e = c[1];if (c.length == 3) {
        d = c[2];
      }b.headerObj = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(f));b.payloadObj = KJUR.jws.JWS.readSafeJSONString(b64utoutf8(e));b.headerPP = JSON.stringify(b.headerObj, null, "  ");if (b.payloadObj == null) {
        b.payloadPP = b64utoutf8(e);
      } else {
        b.payloadPP = JSON.stringify(b.payloadObj, null, "  ");
      }if (d !== undefined) {
        b.sigHex = b64utohex(d);
      }return b;
    };KJUR.jws.JWS.verifyJWT = function (e, l, r) {
      var d = KJUR,
          j = d.jws,
          o = j.JWS,
          n = o.readSafeJSONString,
          p = o.inArray,
          f = o.includedArray;var k = e.split(".");var c = k[0];var i = k[1];var q = c + "." + i;var m = b64utohex(k[2]);var h = n(b64utoutf8(c));var g = n(b64utoutf8(i));if (h.alg === undefined) {
        return false;
      }if (r.alg === undefined) {
        throw "acceptField.alg shall be specified";
      }if (!p(h.alg, r.alg)) {
        return false;
      }if (g.iss !== undefined && _typeof(r.iss) === "object") {
        if (!p(g.iss, r.iss)) {
          return false;
        }
      }if (g.sub !== undefined && _typeof(r.sub) === "object") {
        if (!p(g.sub, r.sub)) {
          return false;
        }
      }if (g.aud !== undefined && _typeof(r.aud) === "object") {
        if (typeof g.aud == "string") {
          if (!p(g.aud, r.aud)) {
            return false;
          }
        } else {
          if (_typeof(g.aud) == "object") {
            if (!f(g.aud, r.aud)) {
              return false;
            }
          }
        }
      }var b = j.IntDate.getNow();if (r.verifyAt !== undefined && typeof r.verifyAt === "number") {
        b = r.verifyAt;
      }if (r.gracePeriod === undefined || typeof r.gracePeriod !== "number") {
        r.gracePeriod = 0;
      }if (g.exp !== undefined && typeof g.exp == "number") {
        if (g.exp + r.gracePeriod < b) {
          return false;
        }
      }if (g.nbf !== undefined && typeof g.nbf == "number") {
        if (b < g.nbf - r.gracePeriod) {
          return false;
        }
      }if (g.iat !== undefined && typeof g.iat == "number") {
        if (b < g.iat - r.gracePeriod) {
          return false;
        }
      }if (g.jti !== undefined && r.jti !== undefined) {
        if (g.jti !== r.jti) {
          return false;
        }
      }if (!o.verify(e, l, r.alg)) {
        return false;
      }return true;
    };KJUR.jws.JWS.includedArray = function (b, a) {
      var c = KJUR.jws.JWS.inArray;if (b === null) {
        return false;
      }if ((typeof b === "undefined" ? "undefined" : _typeof(b)) !== "object") {
        return false;
      }if (typeof b.length !== "number") {
        return false;
      }for (var d = 0; d < b.length; d++) {
        if (!c(b[d], a)) {
          return false;
        }
      }return true;
    };KJUR.jws.JWS.inArray = function (d, b) {
      if (b === null) {
        return false;
      }if ((typeof b === "undefined" ? "undefined" : _typeof(b)) !== "object") {
        return false;
      }if (typeof b.length !== "number") {
        return false;
      }for (var c = 0; c < b.length; c++) {
        if (b[c] == d) {
          return true;
        }
      }return false;
    };KJUR.jws.JWS.jwsalg2sigalg = { HS256: "HmacSHA256", HS384: "HmacSHA384", HS512: "HmacSHA512", RS256: "SHA256withRSA", RS384: "SHA384withRSA", RS512: "SHA512withRSA", ES256: "SHA256withECDSA", ES384: "SHA384withECDSA", PS256: "SHA256withRSAandMGF1", PS384: "SHA384withRSAandMGF1", PS512: "SHA512withRSAandMGF1", none: "none" };KJUR.jws.JWS.isSafeJSONString = function (c, b, d) {
      var e = null;try {
        e = jsonParse(c);if ((typeof e === "undefined" ? "undefined" : _typeof(e)) != "object") {
          return 0;
        }if (e.constructor === Array) {
          return 0;
        }if (b) {
          b[d] = e;
        }return 1;
      } catch (a) {
        return 0;
      }
    };KJUR.jws.JWS.readSafeJSONString = function (b) {
      var c = null;try {
        c = jsonParse(b);if ((typeof c === "undefined" ? "undefined" : _typeof(c)) != "object") {
          return null;
        }if (c.constructor === Array) {
          return null;
        }return c;
      } catch (a) {
        return null;
      }
    };KJUR.jws.JWS.getEncodedSignatureValueFromJWS = function (b) {
      var a = b.match(/^[^.]+\.[^.]+\.([^.]+)$/);if (a == null) {
        throw "JWS signature is not a form of 'Head.Payload.SigValue'.";
      }return a[1];
    };KJUR.jws.JWS.getJWKthumbprint = function (d) {
      if (d.kty !== "RSA" && d.kty !== "EC" && d.kty !== "oct") {
        throw "unsupported algorithm for JWK Thumprint";
      }var a = "{";if (d.kty === "RSA") {
        if (typeof d.n != "string" || typeof d.e != "string") {
          throw "wrong n and e value for RSA key";
        }a += '"e":"' + d.e + '",';a += '"kty":"' + d.kty + '",';a += '"n":"' + d.n + '"}';
      } else {
        if (d.kty === "EC") {
          if (typeof d.crv != "string" || typeof d.x != "string" || typeof d.y != "string") {
            throw "wrong crv, x and y value for EC key";
          }a += '"crv":"' + d.crv + '",';a += '"kty":"' + d.kty + '",';a += '"x":"' + d.x + '",';a += '"y":"' + d.y + '"}';
        } else {
          if (d.kty === "oct") {
            if (typeof d.k != "string") {
              throw "wrong k value for oct(symmetric) key";
            }a += '"kty":"' + d.kty + '",';a += '"k":"' + d.k + '"}';
          }
        }
      }var b = rstrtohex(a);var c = KJUR.crypto.Util.hashHex(b, "sha256");var e = hextob64u(c);return e;
    };KJUR.jws.IntDate = {};KJUR.jws.IntDate.get = function (c) {
      var b = KJUR.jws.IntDate,
          d = b.getNow,
          a = b.getZulu;if (c == "now") {
        return d();
      } else {
        if (c == "now + 1hour") {
          return d() + 60 * 60;
        } else {
          if (c == "now + 1day") {
            return d() + 60 * 60 * 24;
          } else {
            if (c == "now + 1month") {
              return d() + 60 * 60 * 24 * 30;
            } else {
              if (c == "now + 1year") {
                return d() + 60 * 60 * 24 * 365;
              } else {
                if (c.match(/Z$/)) {
                  return a(c);
                } else {
                  if (c.match(/^[0-9]+$/)) {
                    return parseInt(c);
                  }
                }
              }
            }
          }
        }
      }throw "unsupported format: " + c;
    };KJUR.jws.IntDate.getZulu = function (a) {
      return zulutosec(a);
    };KJUR.jws.IntDate.getNow = function () {
      var a = ~~(new Date() / 1000);return a;
    };KJUR.jws.IntDate.intDate2UTCString = function (a) {
      var b = new Date(a * 1000);return b.toUTCString();
    };KJUR.jws.IntDate.intDate2Zulu = function (e) {
      var i = new Date(e * 1000),
          h = ("0000" + i.getUTCFullYear()).slice(-4),
          g = ("00" + (i.getUTCMonth() + 1)).slice(-2),
          b = ("00" + i.getUTCDate()).slice(-2),
          a = ("00" + i.getUTCHours()).slice(-2),
          c = ("00" + i.getUTCMinutes()).slice(-2),
          f = ("00" + i.getUTCSeconds()).slice(-2);return h + g + b + a + c + f + "Z";
    };
    exports.SecureRandom = SecureRandom;
    exports.rng_seed_time = rng_seed_time;
    exports.BigInteger = BigInteger;
    exports.RSAKey = RSAKey;
    var EDSA = KJUR.crypto.EDSA;
    exports.EDSA = EDSA;
    var DSA = KJUR.crypto.DSA;
    exports.DSA = DSA;
    var Signature = KJUR.crypto.Signature;
    exports.Signature = Signature;
    var MessageDigest = KJUR.crypto.MessageDigest;
    exports.MessageDigest = MessageDigest;
    var Mac = KJUR.crypto.Mac;
    exports.Mac = Mac;
    var Cipher = KJUR.crypto.Cipher;
    exports.Cipher = Cipher;
    exports.KEYUTIL = KEYUTIL;
    exports.ASN1HEX = ASN1HEX;
    exports.X509 = X509;
    exports.CryptoJS = CryptoJS;
    
    // ext/base64.js
    
    exports.b64tohex = b64tohex;
    exports.b64toBA = b64toBA;
    
    // base64x.js
    
    exports.stoBA = stoBA;
    exports.BAtos = BAtos;
    exports.BAtohex = BAtohex;
    exports.stohex = stohex;
    exports.stob64 = stob64;
    exports.stob64u = stob64u;
    exports.b64utos = b64utos;
    exports.b64tob64u = b64tob64u;
    exports.b64utob64 = b64utob64;
    exports.hex2b64 = hex2b64;
    exports.hextob64u = hextob64u;
    exports.b64utohex = b64utohex;
    exports.utf8tob64u = utf8tob64u;
    exports.b64utoutf8 = b64utoutf8;
    exports.utf8tob64 = utf8tob64;
    exports.b64toutf8 = b64toutf8;
    exports.utf8tohex = utf8tohex;
    exports.hextoutf8 = hextoutf8;
    exports.hextorstr = hextorstr;
    exports.rstrtohex = rstrtohex;
    exports.hextob64 = hextob64;
    exports.hextob64nl = hextob64nl;
    exports.b64nltohex = b64nltohex;
    exports.hextopem = hextopem;
    exports.pemtohex = pemtohex;
    exports.hextoArrayBuffer = hextoArrayBuffer;
    exports.ArrayBuffertohex = ArrayBuffertohex;
    exports.zulutomsec = zulutomsec;
    exports.zulutosec = zulutosec;
    exports.zulutodate = zulutodate;
    exports.datetozulu = datetozulu;
    exports.uricmptohex = uricmptohex;
    exports.hextouricmp = hextouricmp;
    exports.ipv6tohex = ipv6tohex;
    exports.hextoipv6 = hextoipv6;
    exports.hextoip = hextoip;
    exports.iptohex = iptohex;
    exports.encodeURIComponentAll = encodeURIComponentAll;
    exports.newline_toUnix = newline_toUnix;
    exports.newline_toDos = newline_toDos;
    exports.hextoposhex = hextoposhex;
    exports.intarystrtohex = intarystrtohex;
    exports.strdiffidx = strdiffidx;
    
    // name spaces
    
    exports.KJUR = KJUR;
    
    var _crypto = KJUR.crypto;
    exports.crypto = _crypto;
    var _KJUR = KJUR;
    var asn1 = _KJUR.asn1;
    exports.asn1 = asn1;
    var _KJUR2 = KJUR;
    var jws = _KJUR2.jws;
    exports.jws = jws;
    var _KJUR3 = KJUR;
    var lang = _KJUR3.lang;
    exports.lang = lang;
    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../node_modules/buffer/index.js */ "./node_modules/buffer/index.js").Buffer))
    
    /***/ }),
    
    /***/ "./node_modules/babel-polyfill/lib/index.js":
    /*!**************************************************!*\
      !*** ./node_modules/babel-polyfill/lib/index.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    /* WEBPACK VAR INJECTION */(function(global) {
    
    __webpack_require__(/*! core-js/shim */ "./node_modules/core-js/shim.js");
    
    __webpack_require__(/*! regenerator-runtime/runtime */ "./node_modules/babel-polyfill/node_modules/regenerator-runtime/runtime.js");
    
    __webpack_require__(/*! core-js/fn/regexp/escape */ "./node_modules/core-js/fn/regexp/escape.js");
    
    if (global._babelPolyfill) {
      throw new Error("only one instance of babel-polyfill is allowed");
    }
    global._babelPolyfill = true;
    
    var DEFINE_PROPERTY = "defineProperty";
    function define(O, key, value) {
      O[key] || Object[DEFINE_PROPERTY](O, key, {
        writable: true,
        configurable: true,
        value: value
      });
    }
    
    define(String.prototype, "padLeft", "".padStart);
    define(String.prototype, "padRight", "".padEnd);
    
    "pop,reverse,shift,keys,values,entries,indexOf,every,some,forEach,map,filter,find,findIndex,includes,join,slice,concat,push,splice,unshift,sort,lastIndexOf,reduce,reduceRight,copyWithin,fill".split(",").forEach(function (key) {
      [][key] && define(Array, key, Function.call.bind([][key]));
    });
    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
    
    /***/ }),
    
    /***/ "./node_modules/babel-polyfill/node_modules/regenerator-runtime/runtime.js":
    /*!*********************************************************************************!*\
      !*** ./node_modules/babel-polyfill/node_modules/regenerator-runtime/runtime.js ***!
      \*********************************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    /* WEBPACK VAR INJECTION */(function(global) {/**
     * Copyright (c) 2014, Facebook, Inc.
     * All rights reserved.
     *
     * This source code is licensed under the BSD-style license found in the
     * https://raw.github.com/facebook/regenerator/master/LICENSE file. An
     * additional grant of patent rights can be found in the PATENTS file in
     * the same directory.
     */
    
    !(function(global) {
      "use strict";
    
      var Op = Object.prototype;
      var hasOwn = Op.hasOwnProperty;
      var undefined; // More compressible than void 0.
      var $Symbol = typeof Symbol === "function" ? Symbol : {};
      var iteratorSymbol = $Symbol.iterator || "@@iterator";
      var asyncIteratorSymbol = $Symbol.asyncIterator || "@@asyncIterator";
      var toStringTagSymbol = $Symbol.toStringTag || "@@toStringTag";
    
      var inModule = typeof module === "object";
      var runtime = global.regeneratorRuntime;
      if (runtime) {
        if (inModule) {
          // If regeneratorRuntime is defined globally and we're in a module,
          // make the exports object identical to regeneratorRuntime.
          module.exports = runtime;
        }
        // Don't bother evaluating the rest of this file if the runtime was
        // already defined globally.
        return;
      }
    
      // Define the runtime globally (as expected by generated code) as either
      // module.exports (if we're in a module) or a new, empty object.
      runtime = global.regeneratorRuntime = inModule ? module.exports : {};
    
      function wrap(innerFn, outerFn, self, tryLocsList) {
        // If outerFn provided and outerFn.prototype is a Generator, then outerFn.prototype instanceof Generator.
        var protoGenerator = outerFn && outerFn.prototype instanceof Generator ? outerFn : Generator;
        var generator = Object.create(protoGenerator.prototype);
        var context = new Context(tryLocsList || []);
    
        // The ._invoke method unifies the implementations of the .next,
        // .throw, and .return methods.
        generator._invoke = makeInvokeMethod(innerFn, self, context);
    
        return generator;
      }
      runtime.wrap = wrap;
    
      // Try/catch helper to minimize deoptimizations. Returns a completion
      // record like context.tryEntries[i].completion. This interface could
      // have been (and was previously) designed to take a closure to be
      // invoked without arguments, but in all the cases we care about we
      // already have an existing method we want to call, so there's no need
      // to create a new function object. We can even get away with assuming
      // the method takes exactly one argument, since that happens to be true
      // in every case, so we don't have to touch the arguments object. The
      // only additional allocation required is the completion record, which
      // has a stable shape and so hopefully should be cheap to allocate.
      function tryCatch(fn, obj, arg) {
        try {
          return { type: "normal", arg: fn.call(obj, arg) };
        } catch (err) {
          return { type: "throw", arg: err };
        }
      }
    
      var GenStateSuspendedStart = "suspendedStart";
      var GenStateSuspendedYield = "suspendedYield";
      var GenStateExecuting = "executing";
      var GenStateCompleted = "completed";
    
      // Returning this object from the innerFn has the same effect as
      // breaking out of the dispatch switch statement.
      var ContinueSentinel = {};
    
      // Dummy constructor functions that we use as the .constructor and
      // .constructor.prototype properties for functions that return Generator
      // objects. For full spec compliance, you may wish to configure your
      // minifier not to mangle the names of these two functions.
      function Generator() {}
      function GeneratorFunction() {}
      function GeneratorFunctionPrototype() {}
    
      // This is a polyfill for %IteratorPrototype% for environments that
      // don't natively support it.
      var IteratorPrototype = {};
      IteratorPrototype[iteratorSymbol] = function () {
        return this;
      };
    
      var getProto = Object.getPrototypeOf;
      var NativeIteratorPrototype = getProto && getProto(getProto(values([])));
      if (NativeIteratorPrototype &&
          NativeIteratorPrototype !== Op &&
          hasOwn.call(NativeIteratorPrototype, iteratorSymbol)) {
        // This environment has a native %IteratorPrototype%; use it instead
        // of the polyfill.
        IteratorPrototype = NativeIteratorPrototype;
      }
    
      var Gp = GeneratorFunctionPrototype.prototype =
        Generator.prototype = Object.create(IteratorPrototype);
      GeneratorFunction.prototype = Gp.constructor = GeneratorFunctionPrototype;
      GeneratorFunctionPrototype.constructor = GeneratorFunction;
      GeneratorFunctionPrototype[toStringTagSymbol] =
        GeneratorFunction.displayName = "GeneratorFunction";
    
      // Helper for defining the .next, .throw, and .return methods of the
      // Iterator interface in terms of a single ._invoke method.
      function defineIteratorMethods(prototype) {
        ["next", "throw", "return"].forEach(function(method) {
          prototype[method] = function(arg) {
            return this._invoke(method, arg);
          };
        });
      }
    
      runtime.isGeneratorFunction = function(genFun) {
        var ctor = typeof genFun === "function" && genFun.constructor;
        return ctor
          ? ctor === GeneratorFunction ||
            // For the native GeneratorFunction constructor, the best we can
            // do is to check its .name property.
            (ctor.displayName || ctor.name) === "GeneratorFunction"
          : false;
      };
    
      runtime.mark = function(genFun) {
        if (Object.setPrototypeOf) {
          Object.setPrototypeOf(genFun, GeneratorFunctionPrototype);
        } else {
          genFun.__proto__ = GeneratorFunctionPrototype;
          if (!(toStringTagSymbol in genFun)) {
            genFun[toStringTagSymbol] = "GeneratorFunction";
          }
        }
        genFun.prototype = Object.create(Gp);
        return genFun;
      };
    
      // Within the body of any async function, `await x` is transformed to
      // `yield regeneratorRuntime.awrap(x)`, so that the runtime can test
      // `hasOwn.call(value, "__await")` to determine if the yielded value is
      // meant to be awaited.
      runtime.awrap = function(arg) {
        return { __await: arg };
      };
    
      function AsyncIterator(generator) {
        function invoke(method, arg, resolve, reject) {
          var record = tryCatch(generator[method], generator, arg);
          if (record.type === "throw") {
            reject(record.arg);
          } else {
            var result = record.arg;
            var value = result.value;
            if (value &&
                typeof value === "object" &&
                hasOwn.call(value, "__await")) {
              return Promise.resolve(value.__await).then(function(value) {
                invoke("next", value, resolve, reject);
              }, function(err) {
                invoke("throw", err, resolve, reject);
              });
            }
    
            return Promise.resolve(value).then(function(unwrapped) {
              // When a yielded Promise is resolved, its final value becomes
              // the .value of the Promise<{value,done}> result for the
              // current iteration. If the Promise is rejected, however, the
              // result for this iteration will be rejected with the same
              // reason. Note that rejections of yielded Promises are not
              // thrown back into the generator function, as is the case
              // when an awaited Promise is rejected. This difference in
              // behavior between yield and await is important, because it
              // allows the consumer to decide what to do with the yielded
              // rejection (swallow it and continue, manually .throw it back
              // into the generator, abandon iteration, whatever). With
              // await, by contrast, there is no opportunity to examine the
              // rejection reason outside the generator function, so the
              // only option is to throw it from the await expression, and
              // let the generator function handle the exception.
              result.value = unwrapped;
              resolve(result);
            }, reject);
          }
        }
    
        if (typeof global.process === "object" && global.process.domain) {
          invoke = global.process.domain.bind(invoke);
        }
    
        var previousPromise;
    
        function enqueue(method, arg) {
          function callInvokeWithMethodAndArg() {
            return new Promise(function(resolve, reject) {
              invoke(method, arg, resolve, reject);
            });
          }
    
          return previousPromise =
            // If enqueue has been called before, then we want to wait until
            // all previous Promises have been resolved before calling invoke,
            // so that results are always delivered in the correct order. If
            // enqueue has not been called before, then it is important to
            // call invoke immediately, without waiting on a callback to fire,
            // so that the async generator function has the opportunity to do
            // any necessary setup in a predictable way. This predictability
            // is why the Promise constructor synchronously invokes its
            // executor callback, and why async functions synchronously
            // execute code before the first await. Since we implement simple
            // async functions in terms of async generators, it is especially
            // important to get this right, even though it requires care.
            previousPromise ? previousPromise.then(
              callInvokeWithMethodAndArg,
              // Avoid propagating failures to Promises returned by later
              // invocations of the iterator.
              callInvokeWithMethodAndArg
            ) : callInvokeWithMethodAndArg();
        }
    
        // Define the unified helper method that is used to implement .next,
        // .throw, and .return (see defineIteratorMethods).
        this._invoke = enqueue;
      }
    
      defineIteratorMethods(AsyncIterator.prototype);
      AsyncIterator.prototype[asyncIteratorSymbol] = function () {
        return this;
      };
      runtime.AsyncIterator = AsyncIterator;
    
      // Note that simple async functions are implemented on top of
      // AsyncIterator objects; they just return a Promise for the value of
      // the final result produced by the iterator.
      runtime.async = function(innerFn, outerFn, self, tryLocsList) {
        var iter = new AsyncIterator(
          wrap(innerFn, outerFn, self, tryLocsList)
        );
    
        return runtime.isGeneratorFunction(outerFn)
          ? iter // If outerFn is a generator, return the full iterator.
          : iter.next().then(function(result) {
              return result.done ? result.value : iter.next();
            });
      };
    
      function makeInvokeMethod(innerFn, self, context) {
        var state = GenStateSuspendedStart;
    
        return function invoke(method, arg) {
          if (state === GenStateExecuting) {
            throw new Error("Generator is already running");
          }
    
          if (state === GenStateCompleted) {
            if (method === "throw") {
              throw arg;
            }
    
            // Be forgiving, per 25.3.3.3.3 of the spec:
            // https://people.mozilla.org/~jorendorff/es6-draft.html#sec-generatorresume
            return doneResult();
          }
    
          context.method = method;
          context.arg = arg;
    
          while (true) {
            var delegate = context.delegate;
            if (delegate) {
              var delegateResult = maybeInvokeDelegate(delegate, context);
              if (delegateResult) {
                if (delegateResult === ContinueSentinel) continue;
                return delegateResult;
              }
            }
    
            if (context.method === "next") {
              // Setting context._sent for legacy support of Babel's
              // function.sent implementation.
              context.sent = context._sent = context.arg;
    
            } else if (context.method === "throw") {
              if (state === GenStateSuspendedStart) {
                state = GenStateCompleted;
                throw context.arg;
              }
    
              context.dispatchException(context.arg);
    
            } else if (context.method === "return") {
              context.abrupt("return", context.arg);
            }
    
            state = GenStateExecuting;
    
            var record = tryCatch(innerFn, self, context);
            if (record.type === "normal") {
              // If an exception is thrown from innerFn, we leave state ===
              // GenStateExecuting and loop back for another invocation.
              state = context.done
                ? GenStateCompleted
                : GenStateSuspendedYield;
    
              if (record.arg === ContinueSentinel) {
                continue;
              }
    
              return {
                value: record.arg,
                done: context.done
              };
    
            } else if (record.type === "throw") {
              state = GenStateCompleted;
              // Dispatch the exception by looping back around to the
              // context.dispatchException(context.arg) call above.
              context.method = "throw";
              context.arg = record.arg;
            }
          }
        };
      }
    
      // Call delegate.iterator[context.method](context.arg) and handle the
      // result, either by returning a { value, done } result from the
      // delegate iterator, or by modifying context.method and context.arg,
      // setting context.delegate to null, and returning the ContinueSentinel.
      function maybeInvokeDelegate(delegate, context) {
        var method = delegate.iterator[context.method];
        if (method === undefined) {
          // A .throw or .return when the delegate iterator has no .throw
          // method always terminates the yield* loop.
          context.delegate = null;
    
          if (context.method === "throw") {
            if (delegate.iterator.return) {
              // If the delegate iterator has a return method, give it a
              // chance to clean up.
              context.method = "return";
              context.arg = undefined;
              maybeInvokeDelegate(delegate, context);
    
              if (context.method === "throw") {
                // If maybeInvokeDelegate(context) changed context.method from
                // "return" to "throw", let that override the TypeError below.
                return ContinueSentinel;
              }
            }
    
            context.method = "throw";
            context.arg = new TypeError(
              "The iterator does not provide a 'throw' method");
          }
    
          return ContinueSentinel;
        }
    
        var record = tryCatch(method, delegate.iterator, context.arg);
    
        if (record.type === "throw") {
          context.method = "throw";
          context.arg = record.arg;
          context.delegate = null;
          return ContinueSentinel;
        }
    
        var info = record.arg;
    
        if (! info) {
          context.method = "throw";
          context.arg = new TypeError("iterator result is not an object");
          context.delegate = null;
          return ContinueSentinel;
        }
    
        if (info.done) {
          // Assign the result of the finished delegate to the temporary
          // variable specified by delegate.resultName (see delegateYield).
          context[delegate.resultName] = info.value;
    
          // Resume execution at the desired location (see delegateYield).
          context.next = delegate.nextLoc;
    
          // If context.method was "throw" but the delegate handled the
          // exception, let the outer generator proceed normally. If
          // context.method was "next", forget context.arg since it has been
          // "consumed" by the delegate iterator. If context.method was
          // "return", allow the original .return call to continue in the
          // outer generator.
          if (context.method !== "return") {
            context.method = "next";
            context.arg = undefined;
          }
    
        } else {
          // Re-yield the result returned by the delegate method.
          return info;
        }
    
        // The delegate iterator is finished, so forget it and continue with
        // the outer generator.
        context.delegate = null;
        return ContinueSentinel;
      }
    
      // Define Generator.prototype.{next,throw,return} in terms of the
      // unified ._invoke helper method.
      defineIteratorMethods(Gp);
    
      Gp[toStringTagSymbol] = "Generator";
    
      // A Generator should always return itself as the iterator object when the
      // @@iterator function is called on it. Some browsers' implementations of the
      // iterator prototype chain incorrectly implement this, causing the Generator
      // object to not be returned from this call. This ensures that doesn't happen.
      // See https://github.com/facebook/regenerator/issues/274 for more details.
      Gp[iteratorSymbol] = function() {
        return this;
      };
    
      Gp.toString = function() {
        return "[object Generator]";
      };
    
      function pushTryEntry(locs) {
        var entry = { tryLoc: locs[0] };
    
        if (1 in locs) {
          entry.catchLoc = locs[1];
        }
    
        if (2 in locs) {
          entry.finallyLoc = locs[2];
          entry.afterLoc = locs[3];
        }
    
        this.tryEntries.push(entry);
      }
    
      function resetTryEntry(entry) {
        var record = entry.completion || {};
        record.type = "normal";
        delete record.arg;
        entry.completion = record;
      }
    
      function Context(tryLocsList) {
        // The root entry object (effectively a try statement without a catch
        // or a finally block) gives us a place to store values thrown from
        // locations where there is no enclosing try statement.
        this.tryEntries = [{ tryLoc: "root" }];
        tryLocsList.forEach(pushTryEntry, this);
        this.reset(true);
      }
    
      runtime.keys = function(object) {
        var keys = [];
        for (var key in object) {
          keys.push(key);
        }
        keys.reverse();
    
        // Rather than returning an object with a next method, we keep
        // things simple and return the next function itself.
        return function next() {
          while (keys.length) {
            var key = keys.pop();
            if (key in object) {
              next.value = key;
              next.done = false;
              return next;
            }
          }
    
          // To avoid creating an additional object, we just hang the .value
          // and .done properties off the next function object itself. This
          // also ensures that the minifier will not anonymize the function.
          next.done = true;
          return next;
        };
      };
    
      function values(iterable) {
        if (iterable) {
          var iteratorMethod = iterable[iteratorSymbol];
          if (iteratorMethod) {
            return iteratorMethod.call(iterable);
          }
    
          if (typeof iterable.next === "function") {
            return iterable;
          }
    
          if (!isNaN(iterable.length)) {
            var i = -1, next = function next() {
              while (++i < iterable.length) {
                if (hasOwn.call(iterable, i)) {
                  next.value = iterable[i];
                  next.done = false;
                  return next;
                }
              }
    
              next.value = undefined;
              next.done = true;
    
              return next;
            };
    
            return next.next = next;
          }
        }
    
        // Return an iterator with no values.
        return { next: doneResult };
      }
      runtime.values = values;
    
      function doneResult() {
        return { value: undefined, done: true };
      }
    
      Context.prototype = {
        constructor: Context,
    
        reset: function(skipTempReset) {
          this.prev = 0;
          this.next = 0;
          // Resetting context._sent for legacy support of Babel's
          // function.sent implementation.
          this.sent = this._sent = undefined;
          this.done = false;
          this.delegate = null;
    
          this.method = "next";
          this.arg = undefined;
    
          this.tryEntries.forEach(resetTryEntry);
    
          if (!skipTempReset) {
            for (var name in this) {
              // Not sure about the optimal order of these conditions:
              if (name.charAt(0) === "t" &&
                  hasOwn.call(this, name) &&
                  !isNaN(+name.slice(1))) {
                this[name] = undefined;
              }
            }
          }
        },
    
        stop: function() {
          this.done = true;
    
          var rootEntry = this.tryEntries[0];
          var rootRecord = rootEntry.completion;
          if (rootRecord.type === "throw") {
            throw rootRecord.arg;
          }
    
          return this.rval;
        },
    
        dispatchException: function(exception) {
          if (this.done) {
            throw exception;
          }
    
          var context = this;
          function handle(loc, caught) {
            record.type = "throw";
            record.arg = exception;
            context.next = loc;
    
            if (caught) {
              // If the dispatched exception was caught by a catch block,
              // then let that catch block handle the exception normally.
              context.method = "next";
              context.arg = undefined;
            }
    
            return !! caught;
          }
    
          for (var i = this.tryEntries.length - 1; i >= 0; --i) {
            var entry = this.tryEntries[i];
            var record = entry.completion;
    
            if (entry.tryLoc === "root") {
              // Exception thrown outside of any try block that could handle
              // it, so set the completion value of the entire function to
              // throw the exception.
              return handle("end");
            }
    
            if (entry.tryLoc <= this.prev) {
              var hasCatch = hasOwn.call(entry, "catchLoc");
              var hasFinally = hasOwn.call(entry, "finallyLoc");
    
              if (hasCatch && hasFinally) {
                if (this.prev < entry.catchLoc) {
                  return handle(entry.catchLoc, true);
                } else if (this.prev < entry.finallyLoc) {
                  return handle(entry.finallyLoc);
                }
    
              } else if (hasCatch) {
                if (this.prev < entry.catchLoc) {
                  return handle(entry.catchLoc, true);
                }
    
              } else if (hasFinally) {
                if (this.prev < entry.finallyLoc) {
                  return handle(entry.finallyLoc);
                }
    
              } else {
                throw new Error("try statement without catch or finally");
              }
            }
          }
        },
    
        abrupt: function(type, arg) {
          for (var i = this.tryEntries.length - 1; i >= 0; --i) {
            var entry = this.tryEntries[i];
            if (entry.tryLoc <= this.prev &&
                hasOwn.call(entry, "finallyLoc") &&
                this.prev < entry.finallyLoc) {
              var finallyEntry = entry;
              break;
            }
          }
    
          if (finallyEntry &&
              (type === "break" ||
               type === "continue") &&
              finallyEntry.tryLoc <= arg &&
              arg <= finallyEntry.finallyLoc) {
            // Ignore the finally entry if control is not jumping to a
            // location outside the try/catch block.
            finallyEntry = null;
          }
    
          var record = finallyEntry ? finallyEntry.completion : {};
          record.type = type;
          record.arg = arg;
    
          if (finallyEntry) {
            this.method = "next";
            this.next = finallyEntry.finallyLoc;
            return ContinueSentinel;
          }
    
          return this.complete(record);
        },
    
        complete: function(record, afterLoc) {
          if (record.type === "throw") {
            throw record.arg;
          }
    
          if (record.type === "break" ||
              record.type === "continue") {
            this.next = record.arg;
          } else if (record.type === "return") {
            this.rval = this.arg = record.arg;
            this.method = "return";
            this.next = "end";
          } else if (record.type === "normal" && afterLoc) {
            this.next = afterLoc;
          }
    
          return ContinueSentinel;
        },
    
        finish: function(finallyLoc) {
          for (var i = this.tryEntries.length - 1; i >= 0; --i) {
            var entry = this.tryEntries[i];
            if (entry.finallyLoc === finallyLoc) {
              this.complete(entry.completion, entry.afterLoc);
              resetTryEntry(entry);
              return ContinueSentinel;
            }
          }
        },
    
        "catch": function(tryLoc) {
          for (var i = this.tryEntries.length - 1; i >= 0; --i) {
            var entry = this.tryEntries[i];
            if (entry.tryLoc === tryLoc) {
              var record = entry.completion;
              if (record.type === "throw") {
                var thrown = record.arg;
                resetTryEntry(entry);
              }
              return thrown;
            }
          }
    
          // The context.catch method must only be called with a location
          // argument that corresponds to a known catch block.
          throw new Error("illegal catch attempt");
        },
    
        delegateYield: function(iterable, resultName, nextLoc) {
          this.delegate = {
            iterator: values(iterable),
            resultName: resultName,
            nextLoc: nextLoc
          };
    
          if (this.method === "next") {
            // Deliberately forget the last sent value so that we don't
            // accidentally pass it on to the delegate.
            this.arg = undefined;
          }
    
          return ContinueSentinel;
        }
      };
    })(
      // Among the various tricks for obtaining a reference to the global
      // object, this seems to be the most reliable technique that does not
      // use indirect eval (which violates Content Security Policy).
      typeof global === "object" ? global :
      typeof window === "object" ? window :
      typeof self === "object" ? self : this
    );
    
    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../../../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
    
    /***/ }),
    
    /***/ "./node_modules/base64-js/index.js":
    /*!*****************************************!*\
      !*** ./node_modules/base64-js/index.js ***!
      \*****************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    exports.byteLength = byteLength
    exports.toByteArray = toByteArray
    exports.fromByteArray = fromByteArray
    
    var lookup = []
    var revLookup = []
    var Arr = typeof Uint8Array !== 'undefined' ? Uint8Array : Array
    
    var code = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/'
    for (var i = 0, len = code.length; i < len; ++i) {
      lookup[i] = code[i]
      revLookup[code.charCodeAt(i)] = i
    }
    
    // Support decoding URL-safe base64 strings, as Node.js does.
    // See: https://en.wikipedia.org/wiki/Base64#URL_applications
    revLookup['-'.charCodeAt(0)] = 62
    revLookup['_'.charCodeAt(0)] = 63
    
    function getLens (b64) {
      var len = b64.length
    
      if (len % 4 > 0) {
        throw new Error('Invalid string. Length must be a multiple of 4')
      }
    
      // Trim off extra bytes after placeholder bytes are found
      // See: https://github.com/beatgammit/base64-js/issues/42
      var validLen = b64.indexOf('=')
      if (validLen === -1) validLen = len
    
      var placeHoldersLen = validLen === len
        ? 0
        : 4 - (validLen % 4)
    
      return [validLen, placeHoldersLen]
    }
    
    // base64 is 4/3 + up to two characters of the original data
    function byteLength (b64) {
      var lens = getLens(b64)
      var validLen = lens[0]
      var placeHoldersLen = lens[1]
      return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
    }
    
    function _byteLength (b64, validLen, placeHoldersLen) {
      return ((validLen + placeHoldersLen) * 3 / 4) - placeHoldersLen
    }
    
    function toByteArray (b64) {
      var tmp
      var lens = getLens(b64)
      var validLen = lens[0]
      var placeHoldersLen = lens[1]
    
      var arr = new Arr(_byteLength(b64, validLen, placeHoldersLen))
    
      var curByte = 0
    
      // if there are placeholders, only get up to the last complete 4 chars
      var len = placeHoldersLen > 0
        ? validLen - 4
        : validLen
    
      for (var i = 0; i < len; i += 4) {
        tmp =
          (revLookup[b64.charCodeAt(i)] << 18) |
          (revLookup[b64.charCodeAt(i + 1)] << 12) |
          (revLookup[b64.charCodeAt(i + 2)] << 6) |
          revLookup[b64.charCodeAt(i + 3)]
        arr[curByte++] = (tmp >> 16) & 0xFF
        arr[curByte++] = (tmp >> 8) & 0xFF
        arr[curByte++] = tmp & 0xFF
      }
    
      if (placeHoldersLen === 2) {
        tmp =
          (revLookup[b64.charCodeAt(i)] << 2) |
          (revLookup[b64.charCodeAt(i + 1)] >> 4)
        arr[curByte++] = tmp & 0xFF
      }
    
      if (placeHoldersLen === 1) {
        tmp =
          (revLookup[b64.charCodeAt(i)] << 10) |
          (revLookup[b64.charCodeAt(i + 1)] << 4) |
          (revLookup[b64.charCodeAt(i + 2)] >> 2)
        arr[curByte++] = (tmp >> 8) & 0xFF
        arr[curByte++] = tmp & 0xFF
      }
    
      return arr
    }
    
    function tripletToBase64 (num) {
      return lookup[num >> 18 & 0x3F] +
        lookup[num >> 12 & 0x3F] +
        lookup[num >> 6 & 0x3F] +
        lookup[num & 0x3F]
    }
    
    function encodeChunk (uint8, start, end) {
      var tmp
      var output = []
      for (var i = start; i < end; i += 3) {
        tmp =
          ((uint8[i] << 16) & 0xFF0000) +
          ((uint8[i + 1] << 8) & 0xFF00) +
          (uint8[i + 2] & 0xFF)
        output.push(tripletToBase64(tmp))
      }
      return output.join('')
    }
    
    function fromByteArray (uint8) {
      var tmp
      var len = uint8.length
      var extraBytes = len % 3 // if we have 1 byte left, pad 2 bytes
      var parts = []
      var maxChunkLength = 16383 // must be multiple of 3
    
      // go through the array every three bytes, we'll deal with trailing stuff later
      for (var i = 0, len2 = len - extraBytes; i < len2; i += maxChunkLength) {
        parts.push(encodeChunk(
          uint8, i, (i + maxChunkLength) > len2 ? len2 : (i + maxChunkLength)
        ))
      }
    
      // pad the end with zeros, but make sure to not forget the extra bytes
      if (extraBytes === 1) {
        tmp = uint8[len - 1]
        parts.push(
          lookup[tmp >> 2] +
          lookup[(tmp << 4) & 0x3F] +
          '=='
        )
      } else if (extraBytes === 2) {
        tmp = (uint8[len - 2] << 8) + uint8[len - 1]
        parts.push(
          lookup[tmp >> 10] +
          lookup[(tmp >> 4) & 0x3F] +
          lookup[(tmp << 2) & 0x3F] +
          '='
        )
      }
    
      return parts.join('')
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/buffer/index.js":
    /*!**************************************!*\
      !*** ./node_modules/buffer/index.js ***!
      \**************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    /* WEBPACK VAR INJECTION */(function(global) {/*!
     * The buffer module from node.js, for the browser.
     *
     * @author   Feross Aboukhadijeh  
     * @license  MIT
     */
    /* eslint-disable no-proto */
    
    
    
    var base64 = __webpack_require__(/*! base64-js */ "./node_modules/base64-js/index.js")
    var ieee754 = __webpack_require__(/*! ieee754 */ "./node_modules/ieee754/index.js")
    var isArray = __webpack_require__(/*! isarray */ "./node_modules/buffer/node_modules/isarray/index.js")
    
    exports.Buffer = Buffer
    exports.SlowBuffer = SlowBuffer
    exports.INSPECT_MAX_BYTES = 50
    
    /**
     * If `Buffer.TYPED_ARRAY_SUPPORT`:
     *   === true    Use Uint8Array implementation (fastest)
     *   === false   Use Object implementation (most compatible, even IE6)
     *
     * Browsers that support typed arrays are IE 10+, Firefox 4+, Chrome 7+, Safari 5.1+,
     * Opera 11.6+, iOS 4.2+.
     *
     * Due to various browser bugs, sometimes the Object implementation will be used even
     * when the browser supports typed arrays.
     *
     * Note:
     *
     *   - Firefox 4-29 lacks support for adding new properties to `Uint8Array` instances,
     *     See: https://bugzilla.mozilla.org/show_bug.cgi?id=695438.
     *
     *   - Chrome 9-10 is missing the `TypedArray.prototype.subarray` function.
     *
     *   - IE10 has a broken `TypedArray.prototype.subarray` function which returns arrays of
     *     incorrect length in some situations.
    
     * We detect these buggy browsers and set `Buffer.TYPED_ARRAY_SUPPORT` to `false` so they
     * get the Object implementation, which is slower but behaves correctly.
     */
    Buffer.TYPED_ARRAY_SUPPORT = global.TYPED_ARRAY_SUPPORT !== undefined
      ? global.TYPED_ARRAY_SUPPORT
      : typedArraySupport()
    
    /*
     * Export kMaxLength after typed array support is determined.
     */
    exports.kMaxLength = kMaxLength()
    
    function typedArraySupport () {
      try {
        var arr = new Uint8Array(1)
        arr.__proto__ = {__proto__: Uint8Array.prototype, foo: function () { return 42 }}
        return arr.foo() === 42 && // typed array instances can be augmented
            typeof arr.subarray === 'function' && // chrome 9-10 lack `subarray`
            arr.subarray(1, 1).byteLength === 0 // ie10 has broken `subarray`
      } catch (e) {
        return false
      }
    }
    
    function kMaxLength () {
      return Buffer.TYPED_ARRAY_SUPPORT
        ? 0x7fffffff
        : 0x3fffffff
    }
    
    function createBuffer (that, length) {
      if (kMaxLength() < length) {
        throw new RangeError('Invalid typed array length')
      }
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        // Return an augmented `Uint8Array` instance, for best performance
        that = new Uint8Array(length)
        that.__proto__ = Buffer.prototype
      } else {
        // Fallback: Return an object instance of the Buffer class
        if (that === null) {
          that = new Buffer(length)
        }
        that.length = length
      }
    
      return that
    }
    
    /**
     * The Buffer constructor returns instances of `Uint8Array` that have their
     * prototype changed to `Buffer.prototype`. Furthermore, `Buffer` is a subclass of
     * `Uint8Array`, so the returned instances will have all the node `Buffer` methods
     * and the `Uint8Array` methods. Square bracket notation works as expected -- it
     * returns a single octet.
     *
     * The `Uint8Array` prototype remains unmodified.
     */
    
    function Buffer (arg, encodingOrOffset, length) {
      if (!Buffer.TYPED_ARRAY_SUPPORT && !(this instanceof Buffer)) {
        return new Buffer(arg, encodingOrOffset, length)
      }
    
      // Common case.
      if (typeof arg === 'number') {
        if (typeof encodingOrOffset === 'string') {
          throw new Error(
            'If encoding is specified then the first argument must be a string'
          )
        }
        return allocUnsafe(this, arg)
      }
      return from(this, arg, encodingOrOffset, length)
    }
    
    Buffer.poolSize = 8192 // not used by this implementation
    
    // TODO: Legacy, not needed anymore. Remove in next major version.
    Buffer._augment = function (arr) {
      arr.__proto__ = Buffer.prototype
      return arr
    }
    
    function from (that, value, encodingOrOffset, length) {
      if (typeof value === 'number') {
        throw new TypeError('"value" argument must not be a number')
      }
    
      if (typeof ArrayBuffer !== 'undefined' && value instanceof ArrayBuffer) {
        return fromArrayBuffer(that, value, encodingOrOffset, length)
      }
    
      if (typeof value === 'string') {
        return fromString(that, value, encodingOrOffset)
      }
    
      return fromObject(that, value)
    }
    
    /**
     * Functionally equivalent to Buffer(arg, encoding) but throws a TypeError
     * if value is a number.
     * Buffer.from(str[, encoding])
     * Buffer.from(array)
     * Buffer.from(buffer)
     * Buffer.from(arrayBuffer[, byteOffset[, length]])
     **/
    Buffer.from = function (value, encodingOrOffset, length) {
      return from(null, value, encodingOrOffset, length)
    }
    
    if (Buffer.TYPED_ARRAY_SUPPORT) {
      Buffer.prototype.__proto__ = Uint8Array.prototype
      Buffer.__proto__ = Uint8Array
      if (typeof Symbol !== 'undefined' && Symbol.species &&
          Buffer[Symbol.species] === Buffer) {
        // Fix subarray() in ES2016. See: https://github.com/feross/buffer/pull/97
        Object.defineProperty(Buffer, Symbol.species, {
          value: null,
          configurable: true
        })
      }
    }
    
    function assertSize (size) {
      if (typeof size !== 'number') {
        throw new TypeError('"size" argument must be a number')
      } else if (size < 0) {
        throw new RangeError('"size" argument must not be negative')
      }
    }
    
    function alloc (that, size, fill, encoding) {
      assertSize(size)
      if (size <= 0) {
        return createBuffer(that, size)
      }
      if (fill !== undefined) {
        // Only pay attention to encoding if it's a string. This
        // prevents accidentally sending in a number that would
        // be interpretted as a start offset.
        return typeof encoding === 'string'
          ? createBuffer(that, size).fill(fill, encoding)
          : createBuffer(that, size).fill(fill)
      }
      return createBuffer(that, size)
    }
    
    /**
     * Creates a new filled Buffer instance.
     * alloc(size[, fill[, encoding]])
     **/
    Buffer.alloc = function (size, fill, encoding) {
      return alloc(null, size, fill, encoding)
    }
    
    function allocUnsafe (that, size) {
      assertSize(size)
      that = createBuffer(that, size < 0 ? 0 : checked(size) | 0)
      if (!Buffer.TYPED_ARRAY_SUPPORT) {
        for (var i = 0; i < size; ++i) {
          that[i] = 0
        }
      }
      return that
    }
    
    /**
     * Equivalent to Buffer(num), by default creates a non-zero-filled Buffer instance.
     * */
    Buffer.allocUnsafe = function (size) {
      return allocUnsafe(null, size)
    }
    /**
     * Equivalent to SlowBuffer(num), by default creates a non-zero-filled Buffer instance.
     */
    Buffer.allocUnsafeSlow = function (size) {
      return allocUnsafe(null, size)
    }
    
    function fromString (that, string, encoding) {
      if (typeof encoding !== 'string' || encoding === '') {
        encoding = 'utf8'
      }
    
      if (!Buffer.isEncoding(encoding)) {
        throw new TypeError('"encoding" must be a valid string encoding')
      }
    
      var length = byteLength(string, encoding) | 0
      that = createBuffer(that, length)
    
      var actual = that.write(string, encoding)
    
      if (actual !== length) {
        // Writing a hex string, for example, that contains invalid characters will
        // cause everything after the first invalid character to be ignored. (e.g.
        // 'abxxcd' will be treated as 'ab')
        that = that.slice(0, actual)
      }
    
      return that
    }
    
    function fromArrayLike (that, array) {
      var length = array.length < 0 ? 0 : checked(array.length) | 0
      that = createBuffer(that, length)
      for (var i = 0; i < length; i += 1) {
        that[i] = array[i] & 255
      }
      return that
    }
    
    function fromArrayBuffer (that, array, byteOffset, length) {
      array.byteLength // this throws if `array` is not a valid ArrayBuffer
    
      if (byteOffset < 0 || array.byteLength < byteOffset) {
        throw new RangeError('\'offset\' is out of bounds')
      }
    
      if (array.byteLength < byteOffset + (length || 0)) {
        throw new RangeError('\'length\' is out of bounds')
      }
    
      if (byteOffset === undefined && length === undefined) {
        array = new Uint8Array(array)
      } else if (length === undefined) {
        array = new Uint8Array(array, byteOffset)
      } else {
        array = new Uint8Array(array, byteOffset, length)
      }
    
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        // Return an augmented `Uint8Array` instance, for best performance
        that = array
        that.__proto__ = Buffer.prototype
      } else {
        // Fallback: Return an object instance of the Buffer class
        that = fromArrayLike(that, array)
      }
      return that
    }
    
    function fromObject (that, obj) {
      if (Buffer.isBuffer(obj)) {
        var len = checked(obj.length) | 0
        that = createBuffer(that, len)
    
        if (that.length === 0) {
          return that
        }
    
        obj.copy(that, 0, 0, len)
        return that
      }
    
      if (obj) {
        if ((typeof ArrayBuffer !== 'undefined' &&
            obj.buffer instanceof ArrayBuffer) || 'length' in obj) {
          if (typeof obj.length !== 'number' || isnan(obj.length)) {
            return createBuffer(that, 0)
          }
          return fromArrayLike(that, obj)
        }
    
        if (obj.type === 'Buffer' && isArray(obj.data)) {
          return fromArrayLike(that, obj.data)
        }
      }
    
      throw new TypeError('First argument must be a string, Buffer, ArrayBuffer, Array, or array-like object.')
    }
    
    function checked (length) {
      // Note: cannot use `length < kMaxLength()` here because that fails when
      // length is NaN (which is otherwise coerced to zero.)
      if (length >= kMaxLength()) {
        throw new RangeError('Attempt to allocate Buffer larger than maximum ' +
                             'size: 0x' + kMaxLength().toString(16) + ' bytes')
      }
      return length | 0
    }
    
    function SlowBuffer (length) {
      if (+length != length) { // eslint-disable-line eqeqeq
        length = 0
      }
      return Buffer.alloc(+length)
    }
    
    Buffer.isBuffer = function isBuffer (b) {
      return !!(b != null && b._isBuffer)
    }
    
    Buffer.compare = function compare (a, b) {
      if (!Buffer.isBuffer(a) || !Buffer.isBuffer(b)) {
        throw new TypeError('Arguments must be Buffers')
      }
    
      if (a === b) return 0
    
      var x = a.length
      var y = b.length
    
      for (var i = 0, len = Math.min(x, y); i < len; ++i) {
        if (a[i] !== b[i]) {
          x = a[i]
          y = b[i]
          break
        }
      }
    
      if (x < y) return -1
      if (y < x) return 1
      return 0
    }
    
    Buffer.isEncoding = function isEncoding (encoding) {
      switch (String(encoding).toLowerCase()) {
        case 'hex':
        case 'utf8':
        case 'utf-8':
        case 'ascii':
        case 'latin1':
        case 'binary':
        case 'base64':
        case 'ucs2':
        case 'ucs-2':
        case 'utf16le':
        case 'utf-16le':
          return true
        default:
          return false
      }
    }
    
    Buffer.concat = function concat (list, length) {
      if (!isArray(list)) {
        throw new TypeError('"list" argument must be an Array of Buffers')
      }
    
      if (list.length === 0) {
        return Buffer.alloc(0)
      }
    
      var i
      if (length === undefined) {
        length = 0
        for (i = 0; i < list.length; ++i) {
          length += list[i].length
        }
      }
    
      var buffer = Buffer.allocUnsafe(length)
      var pos = 0
      for (i = 0; i < list.length; ++i) {
        var buf = list[i]
        if (!Buffer.isBuffer(buf)) {
          throw new TypeError('"list" argument must be an Array of Buffers')
        }
        buf.copy(buffer, pos)
        pos += buf.length
      }
      return buffer
    }
    
    function byteLength (string, encoding) {
      if (Buffer.isBuffer(string)) {
        return string.length
      }
      if (typeof ArrayBuffer !== 'undefined' && typeof ArrayBuffer.isView === 'function' &&
          (ArrayBuffer.isView(string) || string instanceof ArrayBuffer)) {
        return string.byteLength
      }
      if (typeof string !== 'string') {
        string = '' + string
      }
    
      var len = string.length
      if (len === 0) return 0
    
      // Use a for loop to avoid recursion
      var loweredCase = false
      for (;;) {
        switch (encoding) {
          case 'ascii':
          case 'latin1':
          case 'binary':
            return len
          case 'utf8':
          case 'utf-8':
          case undefined:
            return utf8ToBytes(string).length
          case 'ucs2':
          case 'ucs-2':
          case 'utf16le':
          case 'utf-16le':
            return len * 2
          case 'hex':
            return len >>> 1
          case 'base64':
            return base64ToBytes(string).length
          default:
            if (loweredCase) return utf8ToBytes(string).length // assume utf8
            encoding = ('' + encoding).toLowerCase()
            loweredCase = true
        }
      }
    }
    Buffer.byteLength = byteLength
    
    function slowToString (encoding, start, end) {
      var loweredCase = false
    
      // No need to verify that "this.length <= MAX_UINT32" since it's a read-only
      // property of a typed array.
    
      // This behaves neither like String nor Uint8Array in that we set start/end
      // to their upper/lower bounds if the value passed is out of range.
      // undefined is handled specially as per ECMA-262 6th Edition,
      // Section 13.3.3.7 Runtime Semantics: KeyedBindingInitialization.
      if (start === undefined || start < 0) {
        start = 0
      }
      // Return early if start > this.length. Done here to prevent potential uint32
      // coercion fail below.
      if (start > this.length) {
        return ''
      }
    
      if (end === undefined || end > this.length) {
        end = this.length
      }
    
      if (end <= 0) {
        return ''
      }
    
      // Force coersion to uint32. This will also coerce falsey/NaN values to 0.
      end >>>= 0
      start >>>= 0
    
      if (end <= start) {
        return ''
      }
    
      if (!encoding) encoding = 'utf8'
    
      while (true) {
        switch (encoding) {
          case 'hex':
            return hexSlice(this, start, end)
    
          case 'utf8':
          case 'utf-8':
            return utf8Slice(this, start, end)
    
          case 'ascii':
            return asciiSlice(this, start, end)
    
          case 'latin1':
          case 'binary':
            return latin1Slice(this, start, end)
    
          case 'base64':
            return base64Slice(this, start, end)
    
          case 'ucs2':
          case 'ucs-2':
          case 'utf16le':
          case 'utf-16le':
            return utf16leSlice(this, start, end)
    
          default:
            if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
            encoding = (encoding + '').toLowerCase()
            loweredCase = true
        }
      }
    }
    
    // The property is used by `Buffer.isBuffer` and `is-buffer` (in Safari 5-7) to detect
    // Buffer instances.
    Buffer.prototype._isBuffer = true
    
    function swap (b, n, m) {
      var i = b[n]
      b[n] = b[m]
      b[m] = i
    }
    
    Buffer.prototype.swap16 = function swap16 () {
      var len = this.length
      if (len % 2 !== 0) {
        throw new RangeError('Buffer size must be a multiple of 16-bits')
      }
      for (var i = 0; i < len; i += 2) {
        swap(this, i, i + 1)
      }
      return this
    }
    
    Buffer.prototype.swap32 = function swap32 () {
      var len = this.length
      if (len % 4 !== 0) {
        throw new RangeError('Buffer size must be a multiple of 32-bits')
      }
      for (var i = 0; i < len; i += 4) {
        swap(this, i, i + 3)
        swap(this, i + 1, i + 2)
      }
      return this
    }
    
    Buffer.prototype.swap64 = function swap64 () {
      var len = this.length
      if (len % 8 !== 0) {
        throw new RangeError('Buffer size must be a multiple of 64-bits')
      }
      for (var i = 0; i < len; i += 8) {
        swap(this, i, i + 7)
        swap(this, i + 1, i + 6)
        swap(this, i + 2, i + 5)
        swap(this, i + 3, i + 4)
      }
      return this
    }
    
    Buffer.prototype.toString = function toString () {
      var length = this.length | 0
      if (length === 0) return ''
      if (arguments.length === 0) return utf8Slice(this, 0, length)
      return slowToString.apply(this, arguments)
    }
    
    Buffer.prototype.equals = function equals (b) {
      if (!Buffer.isBuffer(b)) throw new TypeError('Argument must be a Buffer')
      if (this === b) return true
      return Buffer.compare(this, b) === 0
    }
    
    Buffer.prototype.inspect = function inspect () {
      var str = ''
      var max = exports.INSPECT_MAX_BYTES
      if (this.length > 0) {
        str = this.toString('hex', 0, max).match(/.{2}/g).join(' ')
        if (this.length > max) str += ' ... '
      }
      return ''
    }
    
    Buffer.prototype.compare = function compare (target, start, end, thisStart, thisEnd) {
      if (!Buffer.isBuffer(target)) {
        throw new TypeError('Argument must be a Buffer')
      }
    
      if (start === undefined) {
        start = 0
      }
      if (end === undefined) {
        end = target ? target.length : 0
      }
      if (thisStart === undefined) {
        thisStart = 0
      }
      if (thisEnd === undefined) {
        thisEnd = this.length
      }
    
      if (start < 0 || end > target.length || thisStart < 0 || thisEnd > this.length) {
        throw new RangeError('out of range index')
      }
    
      if (thisStart >= thisEnd && start >= end) {
        return 0
      }
      if (thisStart >= thisEnd) {
        return -1
      }
      if (start >= end) {
        return 1
      }
    
      start >>>= 0
      end >>>= 0
      thisStart >>>= 0
      thisEnd >>>= 0
    
      if (this === target) return 0
    
      var x = thisEnd - thisStart
      var y = end - start
      var len = Math.min(x, y)
    
      var thisCopy = this.slice(thisStart, thisEnd)
      var targetCopy = target.slice(start, end)
    
      for (var i = 0; i < len; ++i) {
        if (thisCopy[i] !== targetCopy[i]) {
          x = thisCopy[i]
          y = targetCopy[i]
          break
        }
      }
    
      if (x < y) return -1
      if (y < x) return 1
      return 0
    }
    
    // Finds either the first index of `val` in `buffer` at offset >= `byteOffset`,
    // OR the last index of `val` in `buffer` at offset <= `byteOffset`.
    //
    // Arguments:
    // - buffer - a Buffer to search
    // - val - a string, Buffer, or number
    // - byteOffset - an index into `buffer`; will be clamped to an int32
    // - encoding - an optional encoding, relevant is val is a string
    // - dir - true for indexOf, false for lastIndexOf
    function bidirectionalIndexOf (buffer, val, byteOffset, encoding, dir) {
      // Empty buffer means no match
      if (buffer.length === 0) return -1
    
      // Normalize byteOffset
      if (typeof byteOffset === 'string') {
        encoding = byteOffset
        byteOffset = 0
      } else if (byteOffset > 0x7fffffff) {
        byteOffset = 0x7fffffff
      } else if (byteOffset < -0x80000000) {
        byteOffset = -0x80000000
      }
      byteOffset = +byteOffset  // Coerce to Number.
      if (isNaN(byteOffset)) {
        // byteOffset: it it's undefined, null, NaN, "foo", etc, search whole buffer
        byteOffset = dir ? 0 : (buffer.length - 1)
      }
    
      // Normalize byteOffset: negative offsets start from the end of the buffer
      if (byteOffset < 0) byteOffset = buffer.length + byteOffset
      if (byteOffset >= buffer.length) {
        if (dir) return -1
        else byteOffset = buffer.length - 1
      } else if (byteOffset < 0) {
        if (dir) byteOffset = 0
        else return -1
      }
    
      // Normalize val
      if (typeof val === 'string') {
        val = Buffer.from(val, encoding)
      }
    
      // Finally, search either indexOf (if dir is true) or lastIndexOf
      if (Buffer.isBuffer(val)) {
        // Special case: looking for empty string/buffer always fails
        if (val.length === 0) {
          return -1
        }
        return arrayIndexOf(buffer, val, byteOffset, encoding, dir)
      } else if (typeof val === 'number') {
        val = val & 0xFF // Search for a byte value [0-255]
        if (Buffer.TYPED_ARRAY_SUPPORT &&
            typeof Uint8Array.prototype.indexOf === 'function') {
          if (dir) {
            return Uint8Array.prototype.indexOf.call(buffer, val, byteOffset)
          } else {
            return Uint8Array.prototype.lastIndexOf.call(buffer, val, byteOffset)
          }
        }
        return arrayIndexOf(buffer, [ val ], byteOffset, encoding, dir)
      }
    
      throw new TypeError('val must be string, number or Buffer')
    }
    
    function arrayIndexOf (arr, val, byteOffset, encoding, dir) {
      var indexSize = 1
      var arrLength = arr.length
      var valLength = val.length
    
      if (encoding !== undefined) {
        encoding = String(encoding).toLowerCase()
        if (encoding === 'ucs2' || encoding === 'ucs-2' ||
            encoding === 'utf16le' || encoding === 'utf-16le') {
          if (arr.length < 2 || val.length < 2) {
            return -1
          }
          indexSize = 2
          arrLength /= 2
          valLength /= 2
          byteOffset /= 2
        }
      }
    
      function read (buf, i) {
        if (indexSize === 1) {
          return buf[i]
        } else {
          return buf.readUInt16BE(i * indexSize)
        }
      }
    
      var i
      if (dir) {
        var foundIndex = -1
        for (i = byteOffset; i < arrLength; i++) {
          if (read(arr, i) === read(val, foundIndex === -1 ? 0 : i - foundIndex)) {
            if (foundIndex === -1) foundIndex = i
            if (i - foundIndex + 1 === valLength) return foundIndex * indexSize
          } else {
            if (foundIndex !== -1) i -= i - foundIndex
            foundIndex = -1
          }
        }
      } else {
        if (byteOffset + valLength > arrLength) byteOffset = arrLength - valLength
        for (i = byteOffset; i >= 0; i--) {
          var found = true
          for (var j = 0; j < valLength; j++) {
            if (read(arr, i + j) !== read(val, j)) {
              found = false
              break
            }
          }
          if (found) return i
        }
      }
    
      return -1
    }
    
    Buffer.prototype.includes = function includes (val, byteOffset, encoding) {
      return this.indexOf(val, byteOffset, encoding) !== -1
    }
    
    Buffer.prototype.indexOf = function indexOf (val, byteOffset, encoding) {
      return bidirectionalIndexOf(this, val, byteOffset, encoding, true)
    }
    
    Buffer.prototype.lastIndexOf = function lastIndexOf (val, byteOffset, encoding) {
      return bidirectionalIndexOf(this, val, byteOffset, encoding, false)
    }
    
    function hexWrite (buf, string, offset, length) {
      offset = Number(offset) || 0
      var remaining = buf.length - offset
      if (!length) {
        length = remaining
      } else {
        length = Number(length)
        if (length > remaining) {
          length = remaining
        }
      }
    
      // must be an even number of digits
      var strLen = string.length
      if (strLen % 2 !== 0) throw new TypeError('Invalid hex string')
    
      if (length > strLen / 2) {
        length = strLen / 2
      }
      for (var i = 0; i < length; ++i) {
        var parsed = parseInt(string.substr(i * 2, 2), 16)
        if (isNaN(parsed)) return i
        buf[offset + i] = parsed
      }
      return i
    }
    
    function utf8Write (buf, string, offset, length) {
      return blitBuffer(utf8ToBytes(string, buf.length - offset), buf, offset, length)
    }
    
    function asciiWrite (buf, string, offset, length) {
      return blitBuffer(asciiToBytes(string), buf, offset, length)
    }
    
    function latin1Write (buf, string, offset, length) {
      return asciiWrite(buf, string, offset, length)
    }
    
    function base64Write (buf, string, offset, length) {
      return blitBuffer(base64ToBytes(string), buf, offset, length)
    }
    
    function ucs2Write (buf, string, offset, length) {
      return blitBuffer(utf16leToBytes(string, buf.length - offset), buf, offset, length)
    }
    
    Buffer.prototype.write = function write (string, offset, length, encoding) {
      // Buffer#write(string)
      if (offset === undefined) {
        encoding = 'utf8'
        length = this.length
        offset = 0
      // Buffer#write(string, encoding)
      } else if (length === undefined && typeof offset === 'string') {
        encoding = offset
        length = this.length
        offset = 0
      // Buffer#write(string, offset[, length][, encoding])
      } else if (isFinite(offset)) {
        offset = offset | 0
        if (isFinite(length)) {
          length = length | 0
          if (encoding === undefined) encoding = 'utf8'
        } else {
          encoding = length
          length = undefined
        }
      // legacy write(string, encoding, offset, length) - remove in v0.13
      } else {
        throw new Error(
          'Buffer.write(string, encoding, offset[, length]) is no longer supported'
        )
      }
    
      var remaining = this.length - offset
      if (length === undefined || length > remaining) length = remaining
    
      if ((string.length > 0 && (length < 0 || offset < 0)) || offset > this.length) {
        throw new RangeError('Attempt to write outside buffer bounds')
      }
    
      if (!encoding) encoding = 'utf8'
    
      var loweredCase = false
      for (;;) {
        switch (encoding) {
          case 'hex':
            return hexWrite(this, string, offset, length)
    
          case 'utf8':
          case 'utf-8':
            return utf8Write(this, string, offset, length)
    
          case 'ascii':
            return asciiWrite(this, string, offset, length)
    
          case 'latin1':
          case 'binary':
            return latin1Write(this, string, offset, length)
    
          case 'base64':
            // Warning: maxLength not taken into account in base64Write
            return base64Write(this, string, offset, length)
    
          case 'ucs2':
          case 'ucs-2':
          case 'utf16le':
          case 'utf-16le':
            return ucs2Write(this, string, offset, length)
    
          default:
            if (loweredCase) throw new TypeError('Unknown encoding: ' + encoding)
            encoding = ('' + encoding).toLowerCase()
            loweredCase = true
        }
      }
    }
    
    Buffer.prototype.toJSON = function toJSON () {
      return {
        type: 'Buffer',
        data: Array.prototype.slice.call(this._arr || this, 0)
      }
    }
    
    function base64Slice (buf, start, end) {
      if (start === 0 && end === buf.length) {
        return base64.fromByteArray(buf)
      } else {
        return base64.fromByteArray(buf.slice(start, end))
      }
    }
    
    function utf8Slice (buf, start, end) {
      end = Math.min(buf.length, end)
      var res = []
    
      var i = start
      while (i < end) {
        var firstByte = buf[i]
        var codePoint = null
        var bytesPerSequence = (firstByte > 0xEF) ? 4
          : (firstByte > 0xDF) ? 3
          : (firstByte > 0xBF) ? 2
          : 1
    
        if (i + bytesPerSequence <= end) {
          var secondByte, thirdByte, fourthByte, tempCodePoint
    
          switch (bytesPerSequence) {
            case 1:
              if (firstByte < 0x80) {
                codePoint = firstByte
              }
              break
            case 2:
              secondByte = buf[i + 1]
              if ((secondByte & 0xC0) === 0x80) {
                tempCodePoint = (firstByte & 0x1F) << 0x6 | (secondByte & 0x3F)
                if (tempCodePoint > 0x7F) {
                  codePoint = tempCodePoint
                }
              }
              break
            case 3:
              secondByte = buf[i + 1]
              thirdByte = buf[i + 2]
              if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80) {
                tempCodePoint = (firstByte & 0xF) << 0xC | (secondByte & 0x3F) << 0x6 | (thirdByte & 0x3F)
                if (tempCodePoint > 0x7FF && (tempCodePoint < 0xD800 || tempCodePoint > 0xDFFF)) {
                  codePoint = tempCodePoint
                }
              }
              break
            case 4:
              secondByte = buf[i + 1]
              thirdByte = buf[i + 2]
              fourthByte = buf[i + 3]
              if ((secondByte & 0xC0) === 0x80 && (thirdByte & 0xC0) === 0x80 && (fourthByte & 0xC0) === 0x80) {
                tempCodePoint = (firstByte & 0xF) << 0x12 | (secondByte & 0x3F) << 0xC | (thirdByte & 0x3F) << 0x6 | (fourthByte & 0x3F)
                if (tempCodePoint > 0xFFFF && tempCodePoint < 0x110000) {
                  codePoint = tempCodePoint
                }
              }
          }
        }
    
        if (codePoint === null) {
          // we did not generate a valid codePoint so insert a
          // replacement char (U+FFFD) and advance only 1 byte
          codePoint = 0xFFFD
          bytesPerSequence = 1
        } else if (codePoint > 0xFFFF) {
          // encode to utf16 (surrogate pair dance)
          codePoint -= 0x10000
          res.push(codePoint >>> 10 & 0x3FF | 0xD800)
          codePoint = 0xDC00 | codePoint & 0x3FF
        }
    
        res.push(codePoint)
        i += bytesPerSequence
      }
    
      return decodeCodePointsArray(res)
    }
    
    // Based on http://stackoverflow.com/a/22747272/680742, the browser with
    // the lowest limit is Chrome, with 0x10000 args.
    // We go 1 magnitude less, for safety
    var MAX_ARGUMENTS_LENGTH = 0x1000
    
    function decodeCodePointsArray (codePoints) {
      var len = codePoints.length
      if (len <= MAX_ARGUMENTS_LENGTH) {
        return String.fromCharCode.apply(String, codePoints) // avoid extra slice()
      }
    
      // Decode in chunks to avoid "call stack size exceeded".
      var res = ''
      var i = 0
      while (i < len) {
        res += String.fromCharCode.apply(
          String,
          codePoints.slice(i, i += MAX_ARGUMENTS_LENGTH)
        )
      }
      return res
    }
    
    function asciiSlice (buf, start, end) {
      var ret = ''
      end = Math.min(buf.length, end)
    
      for (var i = start; i < end; ++i) {
        ret += String.fromCharCode(buf[i] & 0x7F)
      }
      return ret
    }
    
    function latin1Slice (buf, start, end) {
      var ret = ''
      end = Math.min(buf.length, end)
    
      for (var i = start; i < end; ++i) {
        ret += String.fromCharCode(buf[i])
      }
      return ret
    }
    
    function hexSlice (buf, start, end) {
      var len = buf.length
    
      if (!start || start < 0) start = 0
      if (!end || end < 0 || end > len) end = len
    
      var out = ''
      for (var i = start; i < end; ++i) {
        out += toHex(buf[i])
      }
      return out
    }
    
    function utf16leSlice (buf, start, end) {
      var bytes = buf.slice(start, end)
      var res = ''
      for (var i = 0; i < bytes.length; i += 2) {
        res += String.fromCharCode(bytes[i] + bytes[i + 1] * 256)
      }
      return res
    }
    
    Buffer.prototype.slice = function slice (start, end) {
      var len = this.length
      start = ~~start
      end = end === undefined ? len : ~~end
    
      if (start < 0) {
        start += len
        if (start < 0) start = 0
      } else if (start > len) {
        start = len
      }
    
      if (end < 0) {
        end += len
        if (end < 0) end = 0
      } else if (end > len) {
        end = len
      }
    
      if (end < start) end = start
    
      var newBuf
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        newBuf = this.subarray(start, end)
        newBuf.__proto__ = Buffer.prototype
      } else {
        var sliceLen = end - start
        newBuf = new Buffer(sliceLen, undefined)
        for (var i = 0; i < sliceLen; ++i) {
          newBuf[i] = this[i + start]
        }
      }
    
      return newBuf
    }
    
    /*
     * Need to make sure that buffer isn't trying to write out of bounds.
     */
    function checkOffset (offset, ext, length) {
      if ((offset % 1) !== 0 || offset < 0) throw new RangeError('offset is not uint')
      if (offset + ext > length) throw new RangeError('Trying to access beyond buffer length')
    }
    
    Buffer.prototype.readUIntLE = function readUIntLE (offset, byteLength, noAssert) {
      offset = offset | 0
      byteLength = byteLength | 0
      if (!noAssert) checkOffset(offset, byteLength, this.length)
    
      var val = this[offset]
      var mul = 1
      var i = 0
      while (++i < byteLength && (mul *= 0x100)) {
        val += this[offset + i] * mul
      }
    
      return val
    }
    
    Buffer.prototype.readUIntBE = function readUIntBE (offset, byteLength, noAssert) {
      offset = offset | 0
      byteLength = byteLength | 0
      if (!noAssert) {
        checkOffset(offset, byteLength, this.length)
      }
    
      var val = this[offset + --byteLength]
      var mul = 1
      while (byteLength > 0 && (mul *= 0x100)) {
        val += this[offset + --byteLength] * mul
      }
    
      return val
    }
    
    Buffer.prototype.readUInt8 = function readUInt8 (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 1, this.length)
      return this[offset]
    }
    
    Buffer.prototype.readUInt16LE = function readUInt16LE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 2, this.length)
      return this[offset] | (this[offset + 1] << 8)
    }
    
    Buffer.prototype.readUInt16BE = function readUInt16BE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 2, this.length)
      return (this[offset] << 8) | this[offset + 1]
    }
    
    Buffer.prototype.readUInt32LE = function readUInt32LE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 4, this.length)
    
      return ((this[offset]) |
          (this[offset + 1] << 8) |
          (this[offset + 2] << 16)) +
          (this[offset + 3] * 0x1000000)
    }
    
    Buffer.prototype.readUInt32BE = function readUInt32BE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 4, this.length)
    
      return (this[offset] * 0x1000000) +
        ((this[offset + 1] << 16) |
        (this[offset + 2] << 8) |
        this[offset + 3])
    }
    
    Buffer.prototype.readIntLE = function readIntLE (offset, byteLength, noAssert) {
      offset = offset | 0
      byteLength = byteLength | 0
      if (!noAssert) checkOffset(offset, byteLength, this.length)
    
      var val = this[offset]
      var mul = 1
      var i = 0
      while (++i < byteLength && (mul *= 0x100)) {
        val += this[offset + i] * mul
      }
      mul *= 0x80
    
      if (val >= mul) val -= Math.pow(2, 8 * byteLength)
    
      return val
    }
    
    Buffer.prototype.readIntBE = function readIntBE (offset, byteLength, noAssert) {
      offset = offset | 0
      byteLength = byteLength | 0
      if (!noAssert) checkOffset(offset, byteLength, this.length)
    
      var i = byteLength
      var mul = 1
      var val = this[offset + --i]
      while (i > 0 && (mul *= 0x100)) {
        val += this[offset + --i] * mul
      }
      mul *= 0x80
    
      if (val >= mul) val -= Math.pow(2, 8 * byteLength)
    
      return val
    }
    
    Buffer.prototype.readInt8 = function readInt8 (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 1, this.length)
      if (!(this[offset] & 0x80)) return (this[offset])
      return ((0xff - this[offset] + 1) * -1)
    }
    
    Buffer.prototype.readInt16LE = function readInt16LE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 2, this.length)
      var val = this[offset] | (this[offset + 1] << 8)
      return (val & 0x8000) ? val | 0xFFFF0000 : val
    }
    
    Buffer.prototype.readInt16BE = function readInt16BE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 2, this.length)
      var val = this[offset + 1] | (this[offset] << 8)
      return (val & 0x8000) ? val | 0xFFFF0000 : val
    }
    
    Buffer.prototype.readInt32LE = function readInt32LE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 4, this.length)
    
      return (this[offset]) |
        (this[offset + 1] << 8) |
        (this[offset + 2] << 16) |
        (this[offset + 3] << 24)
    }
    
    Buffer.prototype.readInt32BE = function readInt32BE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 4, this.length)
    
      return (this[offset] << 24) |
        (this[offset + 1] << 16) |
        (this[offset + 2] << 8) |
        (this[offset + 3])
    }
    
    Buffer.prototype.readFloatLE = function readFloatLE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 4, this.length)
      return ieee754.read(this, offset, true, 23, 4)
    }
    
    Buffer.prototype.readFloatBE = function readFloatBE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 4, this.length)
      return ieee754.read(this, offset, false, 23, 4)
    }
    
    Buffer.prototype.readDoubleLE = function readDoubleLE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 8, this.length)
      return ieee754.read(this, offset, true, 52, 8)
    }
    
    Buffer.prototype.readDoubleBE = function readDoubleBE (offset, noAssert) {
      if (!noAssert) checkOffset(offset, 8, this.length)
      return ieee754.read(this, offset, false, 52, 8)
    }
    
    function checkInt (buf, value, offset, ext, max, min) {
      if (!Buffer.isBuffer(buf)) throw new TypeError('"buffer" argument must be a Buffer instance')
      if (value > max || value < min) throw new RangeError('"value" argument is out of bounds')
      if (offset + ext > buf.length) throw new RangeError('Index out of range')
    }
    
    Buffer.prototype.writeUIntLE = function writeUIntLE (value, offset, byteLength, noAssert) {
      value = +value
      offset = offset | 0
      byteLength = byteLength | 0
      if (!noAssert) {
        var maxBytes = Math.pow(2, 8 * byteLength) - 1
        checkInt(this, value, offset, byteLength, maxBytes, 0)
      }
    
      var mul = 1
      var i = 0
      this[offset] = value & 0xFF
      while (++i < byteLength && (mul *= 0x100)) {
        this[offset + i] = (value / mul) & 0xFF
      }
    
      return offset + byteLength
    }
    
    Buffer.prototype.writeUIntBE = function writeUIntBE (value, offset, byteLength, noAssert) {
      value = +value
      offset = offset | 0
      byteLength = byteLength | 0
      if (!noAssert) {
        var maxBytes = Math.pow(2, 8 * byteLength) - 1
        checkInt(this, value, offset, byteLength, maxBytes, 0)
      }
    
      var i = byteLength - 1
      var mul = 1
      this[offset + i] = value & 0xFF
      while (--i >= 0 && (mul *= 0x100)) {
        this[offset + i] = (value / mul) & 0xFF
      }
    
      return offset + byteLength
    }
    
    Buffer.prototype.writeUInt8 = function writeUInt8 (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 1, 0xff, 0)
      if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
      this[offset] = (value & 0xff)
      return offset + 1
    }
    
    function objectWriteUInt16 (buf, value, offset, littleEndian) {
      if (value < 0) value = 0xffff + value + 1
      for (var i = 0, j = Math.min(buf.length - offset, 2); i < j; ++i) {
        buf[offset + i] = (value & (0xff << (8 * (littleEndian ? i : 1 - i)))) >>>
          (littleEndian ? i : 1 - i) * 8
      }
    }
    
    Buffer.prototype.writeUInt16LE = function writeUInt16LE (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        this[offset] = (value & 0xff)
        this[offset + 1] = (value >>> 8)
      } else {
        objectWriteUInt16(this, value, offset, true)
      }
      return offset + 2
    }
    
    Buffer.prototype.writeUInt16BE = function writeUInt16BE (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 2, 0xffff, 0)
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        this[offset] = (value >>> 8)
        this[offset + 1] = (value & 0xff)
      } else {
        objectWriteUInt16(this, value, offset, false)
      }
      return offset + 2
    }
    
    function objectWriteUInt32 (buf, value, offset, littleEndian) {
      if (value < 0) value = 0xffffffff + value + 1
      for (var i = 0, j = Math.min(buf.length - offset, 4); i < j; ++i) {
        buf[offset + i] = (value >>> (littleEndian ? i : 3 - i) * 8) & 0xff
      }
    }
    
    Buffer.prototype.writeUInt32LE = function writeUInt32LE (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        this[offset + 3] = (value >>> 24)
        this[offset + 2] = (value >>> 16)
        this[offset + 1] = (value >>> 8)
        this[offset] = (value & 0xff)
      } else {
        objectWriteUInt32(this, value, offset, true)
      }
      return offset + 4
    }
    
    Buffer.prototype.writeUInt32BE = function writeUInt32BE (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 4, 0xffffffff, 0)
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        this[offset] = (value >>> 24)
        this[offset + 1] = (value >>> 16)
        this[offset + 2] = (value >>> 8)
        this[offset + 3] = (value & 0xff)
      } else {
        objectWriteUInt32(this, value, offset, false)
      }
      return offset + 4
    }
    
    Buffer.prototype.writeIntLE = function writeIntLE (value, offset, byteLength, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) {
        var limit = Math.pow(2, 8 * byteLength - 1)
    
        checkInt(this, value, offset, byteLength, limit - 1, -limit)
      }
    
      var i = 0
      var mul = 1
      var sub = 0
      this[offset] = value & 0xFF
      while (++i < byteLength && (mul *= 0x100)) {
        if (value < 0 && sub === 0 && this[offset + i - 1] !== 0) {
          sub = 1
        }
        this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
      }
    
      return offset + byteLength
    }
    
    Buffer.prototype.writeIntBE = function writeIntBE (value, offset, byteLength, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) {
        var limit = Math.pow(2, 8 * byteLength - 1)
    
        checkInt(this, value, offset, byteLength, limit - 1, -limit)
      }
    
      var i = byteLength - 1
      var mul = 1
      var sub = 0
      this[offset + i] = value & 0xFF
      while (--i >= 0 && (mul *= 0x100)) {
        if (value < 0 && sub === 0 && this[offset + i + 1] !== 0) {
          sub = 1
        }
        this[offset + i] = ((value / mul) >> 0) - sub & 0xFF
      }
    
      return offset + byteLength
    }
    
    Buffer.prototype.writeInt8 = function writeInt8 (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 1, 0x7f, -0x80)
      if (!Buffer.TYPED_ARRAY_SUPPORT) value = Math.floor(value)
      if (value < 0) value = 0xff + value + 1
      this[offset] = (value & 0xff)
      return offset + 1
    }
    
    Buffer.prototype.writeInt16LE = function writeInt16LE (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        this[offset] = (value & 0xff)
        this[offset + 1] = (value >>> 8)
      } else {
        objectWriteUInt16(this, value, offset, true)
      }
      return offset + 2
    }
    
    Buffer.prototype.writeInt16BE = function writeInt16BE (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 2, 0x7fff, -0x8000)
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        this[offset] = (value >>> 8)
        this[offset + 1] = (value & 0xff)
      } else {
        objectWriteUInt16(this, value, offset, false)
      }
      return offset + 2
    }
    
    Buffer.prototype.writeInt32LE = function writeInt32LE (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        this[offset] = (value & 0xff)
        this[offset + 1] = (value >>> 8)
        this[offset + 2] = (value >>> 16)
        this[offset + 3] = (value >>> 24)
      } else {
        objectWriteUInt32(this, value, offset, true)
      }
      return offset + 4
    }
    
    Buffer.prototype.writeInt32BE = function writeInt32BE (value, offset, noAssert) {
      value = +value
      offset = offset | 0
      if (!noAssert) checkInt(this, value, offset, 4, 0x7fffffff, -0x80000000)
      if (value < 0) value = 0xffffffff + value + 1
      if (Buffer.TYPED_ARRAY_SUPPORT) {
        this[offset] = (value >>> 24)
        this[offset + 1] = (value >>> 16)
        this[offset + 2] = (value >>> 8)
        this[offset + 3] = (value & 0xff)
      } else {
        objectWriteUInt32(this, value, offset, false)
      }
      return offset + 4
    }
    
    function checkIEEE754 (buf, value, offset, ext, max, min) {
      if (offset + ext > buf.length) throw new RangeError('Index out of range')
      if (offset < 0) throw new RangeError('Index out of range')
    }
    
    function writeFloat (buf, value, offset, littleEndian, noAssert) {
      if (!noAssert) {
        checkIEEE754(buf, value, offset, 4, 3.4028234663852886e+38, -3.4028234663852886e+38)
      }
      ieee754.write(buf, value, offset, littleEndian, 23, 4)
      return offset + 4
    }
    
    Buffer.prototype.writeFloatLE = function writeFloatLE (value, offset, noAssert) {
      return writeFloat(this, value, offset, true, noAssert)
    }
    
    Buffer.prototype.writeFloatBE = function writeFloatBE (value, offset, noAssert) {
      return writeFloat(this, value, offset, false, noAssert)
    }
    
    function writeDouble (buf, value, offset, littleEndian, noAssert) {
      if (!noAssert) {
        checkIEEE754(buf, value, offset, 8, 1.7976931348623157E+308, -1.7976931348623157E+308)
      }
      ieee754.write(buf, value, offset, littleEndian, 52, 8)
      return offset + 8
    }
    
    Buffer.prototype.writeDoubleLE = function writeDoubleLE (value, offset, noAssert) {
      return writeDouble(this, value, offset, true, noAssert)
    }
    
    Buffer.prototype.writeDoubleBE = function writeDoubleBE (value, offset, noAssert) {
      return writeDouble(this, value, offset, false, noAssert)
    }
    
    // copy(targetBuffer, targetStart=0, sourceStart=0, sourceEnd=buffer.length)
    Buffer.prototype.copy = function copy (target, targetStart, start, end) {
      if (!start) start = 0
      if (!end && end !== 0) end = this.length
      if (targetStart >= target.length) targetStart = target.length
      if (!targetStart) targetStart = 0
      if (end > 0 && end < start) end = start
    
      // Copy 0 bytes; we're done
      if (end === start) return 0
      if (target.length === 0 || this.length === 0) return 0
    
      // Fatal error conditions
      if (targetStart < 0) {
        throw new RangeError('targetStart out of bounds')
      }
      if (start < 0 || start >= this.length) throw new RangeError('sourceStart out of bounds')
      if (end < 0) throw new RangeError('sourceEnd out of bounds')
    
      // Are we oob?
      if (end > this.length) end = this.length
      if (target.length - targetStart < end - start) {
        end = target.length - targetStart + start
      }
    
      var len = end - start
      var i
    
      if (this === target && start < targetStart && targetStart < end) {
        // descending copy from end
        for (i = len - 1; i >= 0; --i) {
          target[i + targetStart] = this[i + start]
        }
      } else if (len < 1000 || !Buffer.TYPED_ARRAY_SUPPORT) {
        // ascending copy from start
        for (i = 0; i < len; ++i) {
          target[i + targetStart] = this[i + start]
        }
      } else {
        Uint8Array.prototype.set.call(
          target,
          this.subarray(start, start + len),
          targetStart
        )
      }
    
      return len
    }
    
    // Usage:
    //    buffer.fill(number[, offset[, end]])
    //    buffer.fill(buffer[, offset[, end]])
    //    buffer.fill(string[, offset[, end]][, encoding])
    Buffer.prototype.fill = function fill (val, start, end, encoding) {
      // Handle string cases:
      if (typeof val === 'string') {
        if (typeof start === 'string') {
          encoding = start
          start = 0
          end = this.length
        } else if (typeof end === 'string') {
          encoding = end
          end = this.length
        }
        if (val.length === 1) {
          var code = val.charCodeAt(0)
          if (code < 256) {
            val = code
          }
        }
        if (encoding !== undefined && typeof encoding !== 'string') {
          throw new TypeError('encoding must be a string')
        }
        if (typeof encoding === 'string' && !Buffer.isEncoding(encoding)) {
          throw new TypeError('Unknown encoding: ' + encoding)
        }
      } else if (typeof val === 'number') {
        val = val & 255
      }
    
      // Invalid ranges are not set to a default, so can range check early.
      if (start < 0 || this.length < start || this.length < end) {
        throw new RangeError('Out of range index')
      }
    
      if (end <= start) {
        return this
      }
    
      start = start >>> 0
      end = end === undefined ? this.length : end >>> 0
    
      if (!val) val = 0
    
      var i
      if (typeof val === 'number') {
        for (i = start; i < end; ++i) {
          this[i] = val
        }
      } else {
        var bytes = Buffer.isBuffer(val)
          ? val
          : utf8ToBytes(new Buffer(val, encoding).toString())
        var len = bytes.length
        for (i = 0; i < end - start; ++i) {
          this[i + start] = bytes[i % len]
        }
      }
    
      return this
    }
    
    // HELPER FUNCTIONS
    // ================
    
    var INVALID_BASE64_RE = /[^+\/0-9A-Za-z-_]/g
    
    function base64clean (str) {
      // Node strips out invalid characters like \n and \t from the string, base64-js does not
      str = stringtrim(str).replace(INVALID_BASE64_RE, '')
      // Node converts strings with length < 2 to ''
      if (str.length < 2) return ''
      // Node allows for non-padded base64 strings (missing trailing ===), base64-js does not
      while (str.length % 4 !== 0) {
        str = str + '='
      }
      return str
    }
    
    function stringtrim (str) {
      if (str.trim) return str.trim()
      return str.replace(/^\s+|\s+$/g, '')
    }
    
    function toHex (n) {
      if (n < 16) return '0' + n.toString(16)
      return n.toString(16)
    }
    
    function utf8ToBytes (string, units) {
      units = units || Infinity
      var codePoint
      var length = string.length
      var leadSurrogate = null
      var bytes = []
    
      for (var i = 0; i < length; ++i) {
        codePoint = string.charCodeAt(i)
    
        // is surrogate component
        if (codePoint > 0xD7FF && codePoint < 0xE000) {
          // last char was a lead
          if (!leadSurrogate) {
            // no lead yet
            if (codePoint > 0xDBFF) {
              // unexpected trail
              if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
              continue
            } else if (i + 1 === length) {
              // unpaired lead
              if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
              continue
            }
    
            // valid lead
            leadSurrogate = codePoint
    
            continue
          }
    
          // 2 leads in a row
          if (codePoint < 0xDC00) {
            if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
            leadSurrogate = codePoint
            continue
          }
    
          // valid surrogate pair
          codePoint = (leadSurrogate - 0xD800 << 10 | codePoint - 0xDC00) + 0x10000
        } else if (leadSurrogate) {
          // valid bmp char, but last char was a lead
          if ((units -= 3) > -1) bytes.push(0xEF, 0xBF, 0xBD)
        }
    
        leadSurrogate = null
    
        // encode utf8
        if (codePoint < 0x80) {
          if ((units -= 1) < 0) break
          bytes.push(codePoint)
        } else if (codePoint < 0x800) {
          if ((units -= 2) < 0) break
          bytes.push(
            codePoint >> 0x6 | 0xC0,
            codePoint & 0x3F | 0x80
          )
        } else if (codePoint < 0x10000) {
          if ((units -= 3) < 0) break
          bytes.push(
            codePoint >> 0xC | 0xE0,
            codePoint >> 0x6 & 0x3F | 0x80,
            codePoint & 0x3F | 0x80
          )
        } else if (codePoint < 0x110000) {
          if ((units -= 4) < 0) break
          bytes.push(
            codePoint >> 0x12 | 0xF0,
            codePoint >> 0xC & 0x3F | 0x80,
            codePoint >> 0x6 & 0x3F | 0x80,
            codePoint & 0x3F | 0x80
          )
        } else {
          throw new Error('Invalid code point')
        }
      }
    
      return bytes
    }
    
    function asciiToBytes (str) {
      var byteArray = []
      for (var i = 0; i < str.length; ++i) {
        // Node's code seems to be doing this and not & 0x7F..
        byteArray.push(str.charCodeAt(i) & 0xFF)
      }
      return byteArray
    }
    
    function utf16leToBytes (str, units) {
      var c, hi, lo
      var byteArray = []
      for (var i = 0; i < str.length; ++i) {
        if ((units -= 2) < 0) break
    
        c = str.charCodeAt(i)
        hi = c >> 8
        lo = c % 256
        byteArray.push(lo)
        byteArray.push(hi)
      }
    
      return byteArray
    }
    
    function base64ToBytes (str) {
      return base64.toByteArray(base64clean(str))
    }
    
    function blitBuffer (src, dst, offset, length) {
      for (var i = 0; i < length; ++i) {
        if ((i + offset >= dst.length) || (i >= src.length)) break
        dst[i + offset] = src[i]
      }
      return i
    }
    
    function isnan (val) {
      return val !== val // eslint-disable-line no-self-compare
    }
    
    /* WEBPACK VAR INJECTION */}.call(this, __webpack_require__(/*! ./../webpack/buildin/global.js */ "./node_modules/webpack/buildin/global.js")))
    
    /***/ }),
    
    /***/ "./node_modules/buffer/node_modules/isarray/index.js":
    /*!***********************************************************!*\
      !*** ./node_modules/buffer/node_modules/isarray/index.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    var toString = {}.toString;
    
    module.exports = Array.isArray || function (arr) {
      return toString.call(arr) == '[object Array]';
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/fn/regexp/escape.js":
    /*!**************************************************!*\
      !*** ./node_modules/core-js/fn/regexp/escape.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ../../modules/core.regexp.escape */ "./node_modules/core-js/modules/core.regexp.escape.js");
    module.exports = __webpack_require__(/*! ../../modules/_core */ "./node_modules/core-js/modules/_core.js").RegExp.escape;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_a-function.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_a-function.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = function (it) {
      if (typeof it != 'function') throw TypeError(it + ' is not a function!');
      return it;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_a-number-value.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/_a-number-value.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var cof = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js");
    module.exports = function (it, msg) {
      if (typeof it != 'number' && cof(it) != 'Number') throw TypeError(msg);
      return +it;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_add-to-unscopables.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/_add-to-unscopables.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 22.1.3.31 Array.prototype[@@unscopables]
    var UNSCOPABLES = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('unscopables');
    var ArrayProto = Array.prototype;
    if (ArrayProto[UNSCOPABLES] == undefined) __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js")(ArrayProto, UNSCOPABLES, {});
    module.exports = function (key) {
      ArrayProto[UNSCOPABLES][key] = true;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_advance-string-index.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/_advance-string-index.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var at = __webpack_require__(/*! ./_string-at */ "./node_modules/core-js/modules/_string-at.js")(true);
    
     // `AdvanceStringIndex` abstract operation
    // https://tc39.github.io/ecma262/#sec-advancestringindex
    module.exports = function (S, index, unicode) {
      return index + (unicode ? at(S, index).length : 1);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_an-instance.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_an-instance.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = function (it, Constructor, name, forbiddenField) {
      if (!(it instanceof Constructor) || (forbiddenField !== undefined && forbiddenField in it)) {
        throw TypeError(name + ': incorrect invocation!');
      } return it;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_an-object.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_an-object.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    module.exports = function (it) {
      if (!isObject(it)) throw TypeError(it + ' is not an object!');
      return it;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_array-copy-within.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/_array-copy-within.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    // 22.1.3.3 Array.prototype.copyWithin(target, start, end = this.length)
    
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toAbsoluteIndex = __webpack_require__(/*! ./_to-absolute-index */ "./node_modules/core-js/modules/_to-absolute-index.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    
    module.exports = [].copyWithin || function copyWithin(target /* = 0 */, start /* = 0, end = @length */) {
      var O = toObject(this);
      var len = toLength(O.length);
      var to = toAbsoluteIndex(target, len);
      var from = toAbsoluteIndex(start, len);
      var end = arguments.length > 2 ? arguments[2] : undefined;
      var count = Math.min((end === undefined ? len : toAbsoluteIndex(end, len)) - from, len - to);
      var inc = 1;
      if (from < to && to < from + count) {
        inc = -1;
        from += count - 1;
        to += count - 1;
      }
      while (count-- > 0) {
        if (from in O) O[to] = O[from];
        else delete O[to];
        to += inc;
        from += inc;
      } return O;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_array-fill.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_array-fill.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    // 22.1.3.6 Array.prototype.fill(value, start = 0, end = this.length)
    
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toAbsoluteIndex = __webpack_require__(/*! ./_to-absolute-index */ "./node_modules/core-js/modules/_to-absolute-index.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    module.exports = function fill(value /* , start = 0, end = @length */) {
      var O = toObject(this);
      var length = toLength(O.length);
      var aLen = arguments.length;
      var index = toAbsoluteIndex(aLen > 1 ? arguments[1] : undefined, length);
      var end = aLen > 2 ? arguments[2] : undefined;
      var endPos = end === undefined ? length : toAbsoluteIndex(end, length);
      while (endPos > index) O[index++] = value;
      return O;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_array-from-iterable.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/_array-from-iterable.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var forOf = __webpack_require__(/*! ./_for-of */ "./node_modules/core-js/modules/_for-of.js");
    
    module.exports = function (iter, ITERATOR) {
      var result = [];
      forOf(iter, false, result.push, result, ITERATOR);
      return result;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_array-includes.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/_array-includes.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // false -> Array#indexOf
    // true  -> Array#includes
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var toAbsoluteIndex = __webpack_require__(/*! ./_to-absolute-index */ "./node_modules/core-js/modules/_to-absolute-index.js");
    module.exports = function (IS_INCLUDES) {
      return function ($this, el, fromIndex) {
        var O = toIObject($this);
        var length = toLength(O.length);
        var index = toAbsoluteIndex(fromIndex, length);
        var value;
        // Array#includes uses SameValueZero equality algorithm
        // eslint-disable-next-line no-self-compare
        if (IS_INCLUDES && el != el) while (length > index) {
          value = O[index++];
          // eslint-disable-next-line no-self-compare
          if (value != value) return true;
        // Array#indexOf ignores holes, Array#includes - not
        } else for (;length > index; index++) if (IS_INCLUDES || index in O) {
          if (O[index] === el) return IS_INCLUDES || index || 0;
        } return !IS_INCLUDES && -1;
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_array-methods.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/_array-methods.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 0 -> Array#forEach
    // 1 -> Array#map
    // 2 -> Array#filter
    // 3 -> Array#some
    // 4 -> Array#every
    // 5 -> Array#find
    // 6 -> Array#findIndex
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var IObject = __webpack_require__(/*! ./_iobject */ "./node_modules/core-js/modules/_iobject.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var asc = __webpack_require__(/*! ./_array-species-create */ "./node_modules/core-js/modules/_array-species-create.js");
    module.exports = function (TYPE, $create) {
      var IS_MAP = TYPE == 1;
      var IS_FILTER = TYPE == 2;
      var IS_SOME = TYPE == 3;
      var IS_EVERY = TYPE == 4;
      var IS_FIND_INDEX = TYPE == 6;
      var NO_HOLES = TYPE == 5 || IS_FIND_INDEX;
      var create = $create || asc;
      return function ($this, callbackfn, that) {
        var O = toObject($this);
        var self = IObject(O);
        var f = ctx(callbackfn, that, 3);
        var length = toLength(self.length);
        var index = 0;
        var result = IS_MAP ? create($this, length) : IS_FILTER ? create($this, 0) : undefined;
        var val, res;
        for (;length > index; index++) if (NO_HOLES || index in self) {
          val = self[index];
          res = f(val, index, O);
          if (TYPE) {
            if (IS_MAP) result[index] = res;   // map
            else if (res) switch (TYPE) {
              case 3: return true;             // some
              case 5: return val;              // find
              case 6: return index;            // findIndex
              case 2: result.push(val);        // filter
            } else if (IS_EVERY) return false; // every
          }
        }
        return IS_FIND_INDEX ? -1 : IS_SOME || IS_EVERY ? IS_EVERY : result;
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_array-reduce.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/_array-reduce.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var IObject = __webpack_require__(/*! ./_iobject */ "./node_modules/core-js/modules/_iobject.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    
    module.exports = function (that, callbackfn, aLen, memo, isRight) {
      aFunction(callbackfn);
      var O = toObject(that);
      var self = IObject(O);
      var length = toLength(O.length);
      var index = isRight ? length - 1 : 0;
      var i = isRight ? -1 : 1;
      if (aLen < 2) for (;;) {
        if (index in self) {
          memo = self[index];
          index += i;
          break;
        }
        index += i;
        if (isRight ? index < 0 : length <= index) {
          throw TypeError('Reduce of empty array with no initial value');
        }
      }
      for (;isRight ? index >= 0 : length > index; index += i) if (index in self) {
        memo = callbackfn(memo, self[index], index, O);
      }
      return memo;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_array-species-constructor.js":
    /*!********************************************************************!*\
      !*** ./node_modules/core-js/modules/_array-species-constructor.js ***!
      \********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var isArray = __webpack_require__(/*! ./_is-array */ "./node_modules/core-js/modules/_is-array.js");
    var SPECIES = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('species');
    
    module.exports = function (original) {
      var C;
      if (isArray(original)) {
        C = original.constructor;
        // cross-realm fallback
        if (typeof C == 'function' && (C === Array || isArray(C.prototype))) C = undefined;
        if (isObject(C)) {
          C = C[SPECIES];
          if (C === null) C = undefined;
        }
      } return C === undefined ? Array : C;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_array-species-create.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/_array-species-create.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 9.4.2.3 ArraySpeciesCreate(originalArray, length)
    var speciesConstructor = __webpack_require__(/*! ./_array-species-constructor */ "./node_modules/core-js/modules/_array-species-constructor.js");
    
    module.exports = function (original, length) {
      return new (speciesConstructor(original))(length);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_bind.js":
    /*!***********************************************!*\
      !*** ./node_modules/core-js/modules/_bind.js ***!
      \***********************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var invoke = __webpack_require__(/*! ./_invoke */ "./node_modules/core-js/modules/_invoke.js");
    var arraySlice = [].slice;
    var factories = {};
    
    var construct = function (F, len, args) {
      if (!(len in factories)) {
        for (var n = [], i = 0; i < len; i++) n[i] = 'a[' + i + ']';
        // eslint-disable-next-line no-new-func
        factories[len] = Function('F,a', 'return new F(' + n.join(',') + ')');
      } return factories[len](F, args);
    };
    
    module.exports = Function.bind || function bind(that /* , ...args */) {
      var fn = aFunction(this);
      var partArgs = arraySlice.call(arguments, 1);
      var bound = function (/* args... */) {
        var args = partArgs.concat(arraySlice.call(arguments));
        return this instanceof bound ? construct(fn, args.length, args) : invoke(fn, args, that);
      };
      if (isObject(fn.prototype)) bound.prototype = fn.prototype;
      return bound;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_classof.js":
    /*!**************************************************!*\
      !*** ./node_modules/core-js/modules/_classof.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // getting tag from 19.1.3.6 Object.prototype.toString()
    var cof = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js");
    var TAG = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('toStringTag');
    // ES3 wrong here
    var ARG = cof(function () { return arguments; }()) == 'Arguments';
    
    // fallback for IE11 Script Access Denied error
    var tryGet = function (it, key) {
      try {
        return it[key];
      } catch (e) { /* empty */ }
    };
    
    module.exports = function (it) {
      var O, T, B;
      return it === undefined ? 'Undefined' : it === null ? 'Null'
        // @@toStringTag case
        : typeof (T = tryGet(O = Object(it), TAG)) == 'string' ? T
        // builtinTag case
        : ARG ? cof(O)
        // ES3 arguments fallback
        : (B = cof(O)) == 'Object' && typeof O.callee == 'function' ? 'Arguments' : B;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_cof.js":
    /*!**********************************************!*\
      !*** ./node_modules/core-js/modules/_cof.js ***!
      \**********************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    var toString = {}.toString;
    
    module.exports = function (it) {
      return toString.call(it).slice(8, -1);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_collection-strong.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/_collection-strong.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f;
    var create = __webpack_require__(/*! ./_object-create */ "./node_modules/core-js/modules/_object-create.js");
    var redefineAll = __webpack_require__(/*! ./_redefine-all */ "./node_modules/core-js/modules/_redefine-all.js");
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var anInstance = __webpack_require__(/*! ./_an-instance */ "./node_modules/core-js/modules/_an-instance.js");
    var forOf = __webpack_require__(/*! ./_for-of */ "./node_modules/core-js/modules/_for-of.js");
    var $iterDefine = __webpack_require__(/*! ./_iter-define */ "./node_modules/core-js/modules/_iter-define.js");
    var step = __webpack_require__(/*! ./_iter-step */ "./node_modules/core-js/modules/_iter-step.js");
    var setSpecies = __webpack_require__(/*! ./_set-species */ "./node_modules/core-js/modules/_set-species.js");
    var DESCRIPTORS = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js");
    var fastKey = __webpack_require__(/*! ./_meta */ "./node_modules/core-js/modules/_meta.js").fastKey;
    var validate = __webpack_require__(/*! ./_validate-collection */ "./node_modules/core-js/modules/_validate-collection.js");
    var SIZE = DESCRIPTORS ? '_s' : 'size';
    
    var getEntry = function (that, key) {
      // fast case
      var index = fastKey(key);
      var entry;
      if (index !== 'F') return that._i[index];
      // frozen object case
      for (entry = that._f; entry; entry = entry.n) {
        if (entry.k == key) return entry;
      }
    };
    
    module.exports = {
      getConstructor: function (wrapper, NAME, IS_MAP, ADDER) {
        var C = wrapper(function (that, iterable) {
          anInstance(that, C, NAME, '_i');
          that._t = NAME;         // collection type
          that._i = create(null); // index
          that._f = undefined;    // first entry
          that._l = undefined;    // last entry
          that[SIZE] = 0;         // size
          if (iterable != undefined) forOf(iterable, IS_MAP, that[ADDER], that);
        });
        redefineAll(C.prototype, {
          // 23.1.3.1 Map.prototype.clear()
          // 23.2.3.2 Set.prototype.clear()
          clear: function clear() {
            for (var that = validate(this, NAME), data = that._i, entry = that._f; entry; entry = entry.n) {
              entry.r = true;
              if (entry.p) entry.p = entry.p.n = undefined;
              delete data[entry.i];
            }
            that._f = that._l = undefined;
            that[SIZE] = 0;
          },
          // 23.1.3.3 Map.prototype.delete(key)
          // 23.2.3.4 Set.prototype.delete(value)
          'delete': function (key) {
            var that = validate(this, NAME);
            var entry = getEntry(that, key);
            if (entry) {
              var next = entry.n;
              var prev = entry.p;
              delete that._i[entry.i];
              entry.r = true;
              if (prev) prev.n = next;
              if (next) next.p = prev;
              if (that._f == entry) that._f = next;
              if (that._l == entry) that._l = prev;
              that[SIZE]--;
            } return !!entry;
          },
          // 23.2.3.6 Set.prototype.forEach(callbackfn, thisArg = undefined)
          // 23.1.3.5 Map.prototype.forEach(callbackfn, thisArg = undefined)
          forEach: function forEach(callbackfn /* , that = undefined */) {
            validate(this, NAME);
            var f = ctx(callbackfn, arguments.length > 1 ? arguments[1] : undefined, 3);
            var entry;
            while (entry = entry ? entry.n : this._f) {
              f(entry.v, entry.k, this);
              // revert to the last existing entry
              while (entry && entry.r) entry = entry.p;
            }
          },
          // 23.1.3.7 Map.prototype.has(key)
          // 23.2.3.7 Set.prototype.has(value)
          has: function has(key) {
            return !!getEntry(validate(this, NAME), key);
          }
        });
        if (DESCRIPTORS) dP(C.prototype, 'size', {
          get: function () {
            return validate(this, NAME)[SIZE];
          }
        });
        return C;
      },
      def: function (that, key, value) {
        var entry = getEntry(that, key);
        var prev, index;
        // change existing entry
        if (entry) {
          entry.v = value;
        // create new entry
        } else {
          that._l = entry = {
            i: index = fastKey(key, true), // <- index
            k: key,                        // <- key
            v: value,                      // <- value
            p: prev = that._l,             // <- previous entry
            n: undefined,                  // <- next entry
            r: false                       // <- removed
          };
          if (!that._f) that._f = entry;
          if (prev) prev.n = entry;
          that[SIZE]++;
          // add to index
          if (index !== 'F') that._i[index] = entry;
        } return that;
      },
      getEntry: getEntry,
      setStrong: function (C, NAME, IS_MAP) {
        // add .keys, .values, .entries, [@@iterator]
        // 23.1.3.4, 23.1.3.8, 23.1.3.11, 23.1.3.12, 23.2.3.5, 23.2.3.8, 23.2.3.10, 23.2.3.11
        $iterDefine(C, NAME, function (iterated, kind) {
          this._t = validate(iterated, NAME); // target
          this._k = kind;                     // kind
          this._l = undefined;                // previous
        }, function () {
          var that = this;
          var kind = that._k;
          var entry = that._l;
          // revert to the last existing entry
          while (entry && entry.r) entry = entry.p;
          // get next entry
          if (!that._t || !(that._l = entry = entry ? entry.n : that._t._f)) {
            // or finish the iteration
            that._t = undefined;
            return step(1);
          }
          // return step by kind
          if (kind == 'keys') return step(0, entry.k);
          if (kind == 'values') return step(0, entry.v);
          return step(0, [entry.k, entry.v]);
        }, IS_MAP ? 'entries' : 'values', !IS_MAP, true);
    
        // add [@@species], 23.1.2.2, 23.2.2.2
        setSpecies(NAME);
      }
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_collection-to-json.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/_collection-to-json.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/DavidBruant/Map-Set.prototype.toJSON
    var classof = __webpack_require__(/*! ./_classof */ "./node_modules/core-js/modules/_classof.js");
    var from = __webpack_require__(/*! ./_array-from-iterable */ "./node_modules/core-js/modules/_array-from-iterable.js");
    module.exports = function (NAME) {
      return function toJSON() {
        if (classof(this) != NAME) throw TypeError(NAME + "#toJSON isn't generic");
        return from(this);
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_collection-weak.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/_collection-weak.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var redefineAll = __webpack_require__(/*! ./_redefine-all */ "./node_modules/core-js/modules/_redefine-all.js");
    var getWeak = __webpack_require__(/*! ./_meta */ "./node_modules/core-js/modules/_meta.js").getWeak;
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var anInstance = __webpack_require__(/*! ./_an-instance */ "./node_modules/core-js/modules/_an-instance.js");
    var forOf = __webpack_require__(/*! ./_for-of */ "./node_modules/core-js/modules/_for-of.js");
    var createArrayMethod = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js");
    var $has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var validate = __webpack_require__(/*! ./_validate-collection */ "./node_modules/core-js/modules/_validate-collection.js");
    var arrayFind = createArrayMethod(5);
    var arrayFindIndex = createArrayMethod(6);
    var id = 0;
    
    // fallback for uncaught frozen keys
    var uncaughtFrozenStore = function (that) {
      return that._l || (that._l = new UncaughtFrozenStore());
    };
    var UncaughtFrozenStore = function () {
      this.a = [];
    };
    var findUncaughtFrozen = function (store, key) {
      return arrayFind(store.a, function (it) {
        return it[0] === key;
      });
    };
    UncaughtFrozenStore.prototype = {
      get: function (key) {
        var entry = findUncaughtFrozen(this, key);
        if (entry) return entry[1];
      },
      has: function (key) {
        return !!findUncaughtFrozen(this, key);
      },
      set: function (key, value) {
        var entry = findUncaughtFrozen(this, key);
        if (entry) entry[1] = value;
        else this.a.push([key, value]);
      },
      'delete': function (key) {
        var index = arrayFindIndex(this.a, function (it) {
          return it[0] === key;
        });
        if (~index) this.a.splice(index, 1);
        return !!~index;
      }
    };
    
    module.exports = {
      getConstructor: function (wrapper, NAME, IS_MAP, ADDER) {
        var C = wrapper(function (that, iterable) {
          anInstance(that, C, NAME, '_i');
          that._t = NAME;      // collection type
          that._i = id++;      // collection id
          that._l = undefined; // leak store for uncaught frozen objects
          if (iterable != undefined) forOf(iterable, IS_MAP, that[ADDER], that);
        });
        redefineAll(C.prototype, {
          // 23.3.3.2 WeakMap.prototype.delete(key)
          // 23.4.3.3 WeakSet.prototype.delete(value)
          'delete': function (key) {
            if (!isObject(key)) return false;
            var data = getWeak(key);
            if (data === true) return uncaughtFrozenStore(validate(this, NAME))['delete'](key);
            return data && $has(data, this._i) && delete data[this._i];
          },
          // 23.3.3.4 WeakMap.prototype.has(key)
          // 23.4.3.4 WeakSet.prototype.has(value)
          has: function has(key) {
            if (!isObject(key)) return false;
            var data = getWeak(key);
            if (data === true) return uncaughtFrozenStore(validate(this, NAME)).has(key);
            return data && $has(data, this._i);
          }
        });
        return C;
      },
      def: function (that, key, value) {
        var data = getWeak(anObject(key), true);
        if (data === true) uncaughtFrozenStore(that).set(key, value);
        else data[that._i] = value;
        return that;
      },
      ufstore: uncaughtFrozenStore
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_collection.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_collection.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var redefine = __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js");
    var redefineAll = __webpack_require__(/*! ./_redefine-all */ "./node_modules/core-js/modules/_redefine-all.js");
    var meta = __webpack_require__(/*! ./_meta */ "./node_modules/core-js/modules/_meta.js");
    var forOf = __webpack_require__(/*! ./_for-of */ "./node_modules/core-js/modules/_for-of.js");
    var anInstance = __webpack_require__(/*! ./_an-instance */ "./node_modules/core-js/modules/_an-instance.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var $iterDetect = __webpack_require__(/*! ./_iter-detect */ "./node_modules/core-js/modules/_iter-detect.js");
    var setToStringTag = __webpack_require__(/*! ./_set-to-string-tag */ "./node_modules/core-js/modules/_set-to-string-tag.js");
    var inheritIfRequired = __webpack_require__(/*! ./_inherit-if-required */ "./node_modules/core-js/modules/_inherit-if-required.js");
    
    module.exports = function (NAME, wrapper, methods, common, IS_MAP, IS_WEAK) {
      var Base = global[NAME];
      var C = Base;
      var ADDER = IS_MAP ? 'set' : 'add';
      var proto = C && C.prototype;
      var O = {};
      var fixMethod = function (KEY) {
        var fn = proto[KEY];
        redefine(proto, KEY,
          KEY == 'delete' ? function (a) {
            return IS_WEAK && !isObject(a) ? false : fn.call(this, a === 0 ? 0 : a);
          } : KEY == 'has' ? function has(a) {
            return IS_WEAK && !isObject(a) ? false : fn.call(this, a === 0 ? 0 : a);
          } : KEY == 'get' ? function get(a) {
            return IS_WEAK && !isObject(a) ? undefined : fn.call(this, a === 0 ? 0 : a);
          } : KEY == 'add' ? function add(a) { fn.call(this, a === 0 ? 0 : a); return this; }
            : function set(a, b) { fn.call(this, a === 0 ? 0 : a, b); return this; }
        );
      };
      if (typeof C != 'function' || !(IS_WEAK || proto.forEach && !fails(function () {
        new C().entries().next();
      }))) {
        // create collection constructor
        C = common.getConstructor(wrapper, NAME, IS_MAP, ADDER);
        redefineAll(C.prototype, methods);
        meta.NEED = true;
      } else {
        var instance = new C();
        // early implementations not supports chaining
        var HASNT_CHAINING = instance[ADDER](IS_WEAK ? {} : -0, 1) != instance;
        // V8 ~  Chromium 40- weak-collections throws on primitives, but should return false
        var THROWS_ON_PRIMITIVES = fails(function () { instance.has(1); });
        // most early implementations doesn't supports iterables, most modern - not close it correctly
        var ACCEPT_ITERABLES = $iterDetect(function (iter) { new C(iter); }); // eslint-disable-line no-new
        // for early implementations -0 and +0 not the same
        var BUGGY_ZERO = !IS_WEAK && fails(function () {
          // V8 ~ Chromium 42- fails only with 5+ elements
          var $instance = new C();
          var index = 5;
          while (index--) $instance[ADDER](index, index);
          return !$instance.has(-0);
        });
        if (!ACCEPT_ITERABLES) {
          C = wrapper(function (target, iterable) {
            anInstance(target, C, NAME);
            var that = inheritIfRequired(new Base(), target, C);
            if (iterable != undefined) forOf(iterable, IS_MAP, that[ADDER], that);
            return that;
          });
          C.prototype = proto;
          proto.constructor = C;
        }
        if (THROWS_ON_PRIMITIVES || BUGGY_ZERO) {
          fixMethod('delete');
          fixMethod('has');
          IS_MAP && fixMethod('get');
        }
        if (BUGGY_ZERO || HASNT_CHAINING) fixMethod(ADDER);
        // weak collections should not contains .clear method
        if (IS_WEAK && proto.clear) delete proto.clear;
      }
    
      setToStringTag(C, NAME);
    
      O[NAME] = C;
      $export($export.G + $export.W + $export.F * (C != Base), O);
    
      if (!IS_WEAK) common.setStrong(C, NAME, IS_MAP);
    
      return C;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_core.js":
    /*!***********************************************!*\
      !*** ./node_modules/core-js/modules/_core.js ***!
      \***********************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    var core = module.exports = { version: '2.6.4' };
    if (typeof __e == 'number') __e = core; // eslint-disable-line no-undef
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_create-property.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/_create-property.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $defineProperty = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    var createDesc = __webpack_require__(/*! ./_property-desc */ "./node_modules/core-js/modules/_property-desc.js");
    
    module.exports = function (object, index, value) {
      if (index in object) $defineProperty.f(object, index, createDesc(0, value));
      else object[index] = value;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_ctx.js":
    /*!**********************************************!*\
      !*** ./node_modules/core-js/modules/_ctx.js ***!
      \**********************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // optional / simple context binding
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    module.exports = function (fn, that, length) {
      aFunction(fn);
      if (that === undefined) return fn;
      switch (length) {
        case 1: return function (a) {
          return fn.call(that, a);
        };
        case 2: return function (a, b) {
          return fn.call(that, a, b);
        };
        case 3: return function (a, b, c) {
          return fn.call(that, a, b, c);
        };
      }
      return function (/* ...args */) {
        return fn.apply(that, arguments);
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_date-to-iso-string.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/_date-to-iso-string.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 20.3.4.36 / 15.9.5.43 Date.prototype.toISOString()
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var getTime = Date.prototype.getTime;
    var $toISOString = Date.prototype.toISOString;
    
    var lz = function (num) {
      return num > 9 ? num : '0' + num;
    };
    
    // PhantomJS / old WebKit has a broken implementations
    module.exports = (fails(function () {
      return $toISOString.call(new Date(-5e13 - 1)) != '0385-07-25T07:06:39.999Z';
    }) || !fails(function () {
      $toISOString.call(new Date(NaN));
    })) ? function toISOString() {
      if (!isFinite(getTime.call(this))) throw RangeError('Invalid time value');
      var d = this;
      var y = d.getUTCFullYear();
      var m = d.getUTCMilliseconds();
      var s = y < 0 ? '-' : y > 9999 ? '+' : '';
      return s + ('00000' + Math.abs(y)).slice(s ? -6 : -4) +
        '-' + lz(d.getUTCMonth() + 1) + '-' + lz(d.getUTCDate()) +
        'T' + lz(d.getUTCHours()) + ':' + lz(d.getUTCMinutes()) +
        ':' + lz(d.getUTCSeconds()) + '.' + (m > 99 ? m : '0' + lz(m)) + 'Z';
    } : $toISOString;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_date-to-primitive.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/_date-to-primitive.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    var NUMBER = 'number';
    
    module.exports = function (hint) {
      if (hint !== 'string' && hint !== NUMBER && hint !== 'default') throw TypeError('Incorrect hint');
      return toPrimitive(anObject(this), hint != NUMBER);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_defined.js":
    /*!**************************************************!*\
      !*** ./node_modules/core-js/modules/_defined.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // 7.2.1 RequireObjectCoercible(argument)
    module.exports = function (it) {
      if (it == undefined) throw TypeError("Can't call method on  " + it);
      return it;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_descriptors.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_descriptors.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // Thank's IE8 for his funny defineProperty
    module.exports = !__webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      return Object.defineProperty({}, 'a', { get: function () { return 7; } }).a != 7;
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_dom-create.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_dom-create.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var document = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").document;
    // typeof document.createElement is 'object' in old IE
    var is = isObject(document) && isObject(document.createElement);
    module.exports = function (it) {
      return is ? document.createElement(it) : {};
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_enum-bug-keys.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/_enum-bug-keys.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // IE 8- don't enum bug keys
    module.exports = (
      'constructor,hasOwnProperty,isPrototypeOf,propertyIsEnumerable,toLocaleString,toString,valueOf'
    ).split(',');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_enum-keys.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_enum-keys.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // all enumerable object keys, includes symbols
    var getKeys = __webpack_require__(/*! ./_object-keys */ "./node_modules/core-js/modules/_object-keys.js");
    var gOPS = __webpack_require__(/*! ./_object-gops */ "./node_modules/core-js/modules/_object-gops.js");
    var pIE = __webpack_require__(/*! ./_object-pie */ "./node_modules/core-js/modules/_object-pie.js");
    module.exports = function (it) {
      var result = getKeys(it);
      var getSymbols = gOPS.f;
      if (getSymbols) {
        var symbols = getSymbols(it);
        var isEnum = pIE.f;
        var i = 0;
        var key;
        while (symbols.length > i) if (isEnum.call(it, key = symbols[i++])) result.push(key);
      } return result;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_export.js":
    /*!*************************************************!*\
      !*** ./node_modules/core-js/modules/_export.js ***!
      \*************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var core = __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js");
    var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
    var redefine = __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js");
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var PROTOTYPE = 'prototype';
    
    var $export = function (type, name, source) {
      var IS_FORCED = type & $export.F;
      var IS_GLOBAL = type & $export.G;
      var IS_STATIC = type & $export.S;
      var IS_PROTO = type & $export.P;
      var IS_BIND = type & $export.B;
      var target = IS_GLOBAL ? global : IS_STATIC ? global[name] || (global[name] = {}) : (global[name] || {})[PROTOTYPE];
      var exports = IS_GLOBAL ? core : core[name] || (core[name] = {});
      var expProto = exports[PROTOTYPE] || (exports[PROTOTYPE] = {});
      var key, own, out, exp;
      if (IS_GLOBAL) source = name;
      for (key in source) {
        // contains in native
        own = !IS_FORCED && target && target[key] !== undefined;
        // export native or passed
        out = (own ? target : source)[key];
        // bind timers to global for call from export context
        exp = IS_BIND && own ? ctx(out, global) : IS_PROTO && typeof out == 'function' ? ctx(Function.call, out) : out;
        // extend global
        if (target) redefine(target, key, out, type & $export.U);
        // export
        if (exports[key] != out) hide(exports, key, exp);
        if (IS_PROTO && expProto[key] != out) expProto[key] = out;
      }
    };
    global.core = core;
    // type bitmap
    $export.F = 1;   // forced
    $export.G = 2;   // global
    $export.S = 4;   // static
    $export.P = 8;   // proto
    $export.B = 16;  // bind
    $export.W = 32;  // wrap
    $export.U = 64;  // safe
    $export.R = 128; // real proto method for `library`
    module.exports = $export;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_fails-is-regexp.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/_fails-is-regexp.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var MATCH = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('match');
    module.exports = function (KEY) {
      var re = /./;
      try {
        '/./'[KEY](re);
      } catch (e) {
        try {
          re[MATCH] = false;
          return !'/./'[KEY](re);
        } catch (f) { /* empty */ }
      } return true;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_fails.js":
    /*!************************************************!*\
      !*** ./node_modules/core-js/modules/_fails.js ***!
      \************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = function (exec) {
      try {
        return !!exec();
      } catch (e) {
        return true;
      }
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_fix-re-wks.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_fix-re-wks.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    __webpack_require__(/*! ./es6.regexp.exec */ "./node_modules/core-js/modules/es6.regexp.exec.js");
    var redefine = __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js");
    var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    var wks = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js");
    var regexpExec = __webpack_require__(/*! ./_regexp-exec */ "./node_modules/core-js/modules/_regexp-exec.js");
    
    var SPECIES = wks('species');
    
    var REPLACE_SUPPORTS_NAMED_GROUPS = !fails(function () {
      // #replace needs built-in support for named groups.
      // #match works fine because it just return the exec results, even if it has
      // a "grops" property.
      var re = /./;
      re.exec = function () {
        var result = [];
        result.groups = { a: '7' };
        return result;
      };
      return ''.replace(re, '$') !== '7';
    });
    
    var SPLIT_WORKS_WITH_OVERWRITTEN_EXEC = (function () {
      // Chrome 51 has a buggy "split" implementation when RegExp#exec !== nativeExec
      var re = /(?:)/;
      var originalExec = re.exec;
      re.exec = function () { return originalExec.apply(this, arguments); };
      var result = 'ab'.split(re);
      return result.length === 2 && result[0] === 'a' && result[1] === 'b';
    })();
    
    module.exports = function (KEY, length, exec) {
      var SYMBOL = wks(KEY);
    
      var DELEGATES_TO_SYMBOL = !fails(function () {
        // String methods call symbol-named RegEp methods
        var O = {};
        O[SYMBOL] = function () { return 7; };
        return ''[KEY](O) != 7;
      });
    
      var DELEGATES_TO_EXEC = DELEGATES_TO_SYMBOL ? !fails(function () {
        // Symbol-named RegExp methods call .exec
        var execCalled = false;
        var re = /a/;
        re.exec = function () { execCalled = true; return null; };
        if (KEY === 'split') {
          // RegExp[@@split] doesn't call the regex's exec method, but first creates
          // a new one. We need to return the patched regex when creating the new one.
          re.constructor = {};
          re.constructor[SPECIES] = function () { return re; };
        }
        re[SYMBOL]('');
        return !execCalled;
      }) : undefined;
    
      if (
        !DELEGATES_TO_SYMBOL ||
        !DELEGATES_TO_EXEC ||
        (KEY === 'replace' && !REPLACE_SUPPORTS_NAMED_GROUPS) ||
        (KEY === 'split' && !SPLIT_WORKS_WITH_OVERWRITTEN_EXEC)
      ) {
        var nativeRegExpMethod = /./[SYMBOL];
        var fns = exec(
          defined,
          SYMBOL,
          ''[KEY],
          function maybeCallNative(nativeMethod, regexp, str, arg2, forceStringMethod) {
            if (regexp.exec === regexpExec) {
              if (DELEGATES_TO_SYMBOL && !forceStringMethod) {
                // The native String method already delegates to @@method (this
                // polyfilled function), leasing to infinite recursion.
                // We avoid it by directly calling the native @@method method.
                return { done: true, value: nativeRegExpMethod.call(regexp, str, arg2) };
              }
              return { done: true, value: nativeMethod.call(str, regexp, arg2) };
            }
            return { done: false };
          }
        );
        var strfn = fns[0];
        var rxfn = fns[1];
    
        redefine(String.prototype, KEY, strfn);
        hide(RegExp.prototype, SYMBOL, length == 2
          // 21.2.5.8 RegExp.prototype[@@replace](string, replaceValue)
          // 21.2.5.11 RegExp.prototype[@@split](string, limit)
          ? function (string, arg) { return rxfn.call(string, this, arg); }
          // 21.2.5.6 RegExp.prototype[@@match](string)
          // 21.2.5.9 RegExp.prototype[@@search](string)
          : function (string) { return rxfn.call(string, this); }
        );
      }
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_flags.js":
    /*!************************************************!*\
      !*** ./node_modules/core-js/modules/_flags.js ***!
      \************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 21.2.5.3 get RegExp.prototype.flags
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    module.exports = function () {
      var that = anObject(this);
      var result = '';
      if (that.global) result += 'g';
      if (that.ignoreCase) result += 'i';
      if (that.multiline) result += 'm';
      if (that.unicode) result += 'u';
      if (that.sticky) result += 'y';
      return result;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_flatten-into-array.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/_flatten-into-array.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://tc39.github.io/proposal-flatMap/#sec-FlattenIntoArray
    var isArray = __webpack_require__(/*! ./_is-array */ "./node_modules/core-js/modules/_is-array.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var IS_CONCAT_SPREADABLE = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('isConcatSpreadable');
    
    function flattenIntoArray(target, original, source, sourceLen, start, depth, mapper, thisArg) {
      var targetIndex = start;
      var sourceIndex = 0;
      var mapFn = mapper ? ctx(mapper, thisArg, 3) : false;
      var element, spreadable;
    
      while (sourceIndex < sourceLen) {
        if (sourceIndex in source) {
          element = mapFn ? mapFn(source[sourceIndex], sourceIndex, original) : source[sourceIndex];
    
          spreadable = false;
          if (isObject(element)) {
            spreadable = element[IS_CONCAT_SPREADABLE];
            spreadable = spreadable !== undefined ? !!spreadable : isArray(element);
          }
    
          if (spreadable && depth > 0) {
            targetIndex = flattenIntoArray(target, original, element, toLength(element.length), targetIndex, depth - 1) - 1;
          } else {
            if (targetIndex >= 0x1fffffffffffff) throw TypeError();
            target[targetIndex] = element;
          }
    
          targetIndex++;
        }
        sourceIndex++;
      }
      return targetIndex;
    }
    
    module.exports = flattenIntoArray;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_for-of.js":
    /*!*************************************************!*\
      !*** ./node_modules/core-js/modules/_for-of.js ***!
      \*************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var call = __webpack_require__(/*! ./_iter-call */ "./node_modules/core-js/modules/_iter-call.js");
    var isArrayIter = __webpack_require__(/*! ./_is-array-iter */ "./node_modules/core-js/modules/_is-array-iter.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var getIterFn = __webpack_require__(/*! ./core.get-iterator-method */ "./node_modules/core-js/modules/core.get-iterator-method.js");
    var BREAK = {};
    var RETURN = {};
    var exports = module.exports = function (iterable, entries, fn, that, ITERATOR) {
      var iterFn = ITERATOR ? function () { return iterable; } : getIterFn(iterable);
      var f = ctx(fn, that, entries ? 2 : 1);
      var index = 0;
      var length, step, iterator, result;
      if (typeof iterFn != 'function') throw TypeError(iterable + ' is not iterable!');
      // fast case for arrays with default iterator
      if (isArrayIter(iterFn)) for (length = toLength(iterable.length); length > index; index++) {
        result = entries ? f(anObject(step = iterable[index])[0], step[1]) : f(iterable[index]);
        if (result === BREAK || result === RETURN) return result;
      } else for (iterator = iterFn.call(iterable); !(step = iterator.next()).done;) {
        result = call(iterator, f, step.value, entries);
        if (result === BREAK || result === RETURN) return result;
      }
    };
    exports.BREAK = BREAK;
    exports.RETURN = RETURN;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_function-to-string.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/_function-to-string.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    module.exports = __webpack_require__(/*! ./_shared */ "./node_modules/core-js/modules/_shared.js")('native-function-to-string', Function.toString);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_global.js":
    /*!*************************************************!*\
      !*** ./node_modules/core-js/modules/_global.js ***!
      \*************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // https://github.com/zloirock/core-js/issues/86#issuecomment-115759028
    var global = module.exports = typeof window != 'undefined' && window.Math == Math
      ? window : typeof self != 'undefined' && self.Math == Math ? self
      // eslint-disable-next-line no-new-func
      : Function('return this')();
    if (typeof __g == 'number') __g = global; // eslint-disable-line no-undef
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_has.js":
    /*!**********************************************!*\
      !*** ./node_modules/core-js/modules/_has.js ***!
      \**********************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    var hasOwnProperty = {}.hasOwnProperty;
    module.exports = function (it, key) {
      return hasOwnProperty.call(it, key);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_hide.js":
    /*!***********************************************!*\
      !*** ./node_modules/core-js/modules/_hide.js ***!
      \***********************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    var createDesc = __webpack_require__(/*! ./_property-desc */ "./node_modules/core-js/modules/_property-desc.js");
    module.exports = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") ? function (object, key, value) {
      return dP.f(object, key, createDesc(1, value));
    } : function (object, key, value) {
      object[key] = value;
      return object;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_html.js":
    /*!***********************************************!*\
      !*** ./node_modules/core-js/modules/_html.js ***!
      \***********************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var document = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").document;
    module.exports = document && document.documentElement;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_ie8-dom-define.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/_ie8-dom-define.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    module.exports = !__webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") && !__webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      return Object.defineProperty(__webpack_require__(/*! ./_dom-create */ "./node_modules/core-js/modules/_dom-create.js")('div'), 'a', { get: function () { return 7; } }).a != 7;
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_inherit-if-required.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/_inherit-if-required.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var setPrototypeOf = __webpack_require__(/*! ./_set-proto */ "./node_modules/core-js/modules/_set-proto.js").set;
    module.exports = function (that, target, C) {
      var S = target.constructor;
      var P;
      if (S !== C && typeof S == 'function' && (P = S.prototype) !== C.prototype && isObject(P) && setPrototypeOf) {
        setPrototypeOf(that, P);
      } return that;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_invoke.js":
    /*!*************************************************!*\
      !*** ./node_modules/core-js/modules/_invoke.js ***!
      \*************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // fast apply, http://jsperf.lnkit.com/fast-apply/5
    module.exports = function (fn, args, that) {
      var un = that === undefined;
      switch (args.length) {
        case 0: return un ? fn()
                          : fn.call(that);
        case 1: return un ? fn(args[0])
                          : fn.call(that, args[0]);
        case 2: return un ? fn(args[0], args[1])
                          : fn.call(that, args[0], args[1]);
        case 3: return un ? fn(args[0], args[1], args[2])
                          : fn.call(that, args[0], args[1], args[2]);
        case 4: return un ? fn(args[0], args[1], args[2], args[3])
                          : fn.call(that, args[0], args[1], args[2], args[3]);
      } return fn.apply(that, args);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_iobject.js":
    /*!**************************************************!*\
      !*** ./node_modules/core-js/modules/_iobject.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // fallback for non-array-like ES3 and non-enumerable old V8 strings
    var cof = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js");
    // eslint-disable-next-line no-prototype-builtins
    module.exports = Object('z').propertyIsEnumerable(0) ? Object : function (it) {
      return cof(it) == 'String' ? it.split('') : Object(it);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_is-array-iter.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/_is-array-iter.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // check on default Array iterator
    var Iterators = __webpack_require__(/*! ./_iterators */ "./node_modules/core-js/modules/_iterators.js");
    var ITERATOR = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('iterator');
    var ArrayProto = Array.prototype;
    
    module.exports = function (it) {
      return it !== undefined && (Iterators.Array === it || ArrayProto[ITERATOR] === it);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_is-array.js":
    /*!***************************************************!*\
      !*** ./node_modules/core-js/modules/_is-array.js ***!
      \***************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 7.2.2 IsArray(argument)
    var cof = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js");
    module.exports = Array.isArray || function isArray(arg) {
      return cof(arg) == 'Array';
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_is-integer.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_is-integer.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.1.2.3 Number.isInteger(number)
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var floor = Math.floor;
    module.exports = function isInteger(it) {
      return !isObject(it) && isFinite(it) && floor(it) === it;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_is-object.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_is-object.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = function (it) {
      return typeof it === 'object' ? it !== null : typeof it === 'function';
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_is-regexp.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_is-regexp.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 7.2.8 IsRegExp(argument)
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var cof = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js");
    var MATCH = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('match');
    module.exports = function (it) {
      var isRegExp;
      return isObject(it) && ((isRegExp = it[MATCH]) !== undefined ? !!isRegExp : cof(it) == 'RegExp');
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_iter-call.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_iter-call.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // call something on iterator step with safe closing on error
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    module.exports = function (iterator, fn, value, entries) {
      try {
        return entries ? fn(anObject(value)[0], value[1]) : fn(value);
      // 7.4.6 IteratorClose(iterator, completion)
      } catch (e) {
        var ret = iterator['return'];
        if (ret !== undefined) anObject(ret.call(iterator));
        throw e;
      }
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_iter-create.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_iter-create.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var create = __webpack_require__(/*! ./_object-create */ "./node_modules/core-js/modules/_object-create.js");
    var descriptor = __webpack_require__(/*! ./_property-desc */ "./node_modules/core-js/modules/_property-desc.js");
    var setToStringTag = __webpack_require__(/*! ./_set-to-string-tag */ "./node_modules/core-js/modules/_set-to-string-tag.js");
    var IteratorPrototype = {};
    
    // 25.1.2.1.1 %IteratorPrototype%[@@iterator]()
    __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js")(IteratorPrototype, __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('iterator'), function () { return this; });
    
    module.exports = function (Constructor, NAME, next) {
      Constructor.prototype = create(IteratorPrototype, { next: descriptor(1, next) });
      setToStringTag(Constructor, NAME + ' Iterator');
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_iter-define.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_iter-define.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var LIBRARY = __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var redefine = __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js");
    var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
    var Iterators = __webpack_require__(/*! ./_iterators */ "./node_modules/core-js/modules/_iterators.js");
    var $iterCreate = __webpack_require__(/*! ./_iter-create */ "./node_modules/core-js/modules/_iter-create.js");
    var setToStringTag = __webpack_require__(/*! ./_set-to-string-tag */ "./node_modules/core-js/modules/_set-to-string-tag.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var ITERATOR = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('iterator');
    var BUGGY = !([].keys && 'next' in [].keys()); // Safari has buggy iterators w/o `next`
    var FF_ITERATOR = '@@iterator';
    var KEYS = 'keys';
    var VALUES = 'values';
    
    var returnThis = function () { return this; };
    
    module.exports = function (Base, NAME, Constructor, next, DEFAULT, IS_SET, FORCED) {
      $iterCreate(Constructor, NAME, next);
      var getMethod = function (kind) {
        if (!BUGGY && kind in proto) return proto[kind];
        switch (kind) {
          case KEYS: return function keys() { return new Constructor(this, kind); };
          case VALUES: return function values() { return new Constructor(this, kind); };
        } return function entries() { return new Constructor(this, kind); };
      };
      var TAG = NAME + ' Iterator';
      var DEF_VALUES = DEFAULT == VALUES;
      var VALUES_BUG = false;
      var proto = Base.prototype;
      var $native = proto[ITERATOR] || proto[FF_ITERATOR] || DEFAULT && proto[DEFAULT];
      var $default = $native || getMethod(DEFAULT);
      var $entries = DEFAULT ? !DEF_VALUES ? $default : getMethod('entries') : undefined;
      var $anyNative = NAME == 'Array' ? proto.entries || $native : $native;
      var methods, key, IteratorPrototype;
      // Fix native
      if ($anyNative) {
        IteratorPrototype = getPrototypeOf($anyNative.call(new Base()));
        if (IteratorPrototype !== Object.prototype && IteratorPrototype.next) {
          // Set @@toStringTag to native iterators
          setToStringTag(IteratorPrototype, TAG, true);
          // fix for some old engines
          if (!LIBRARY && typeof IteratorPrototype[ITERATOR] != 'function') hide(IteratorPrototype, ITERATOR, returnThis);
        }
      }
      // fix Array#{values, @@iterator}.name in V8 / FF
      if (DEF_VALUES && $native && $native.name !== VALUES) {
        VALUES_BUG = true;
        $default = function values() { return $native.call(this); };
      }
      // Define iterator
      if ((!LIBRARY || FORCED) && (BUGGY || VALUES_BUG || !proto[ITERATOR])) {
        hide(proto, ITERATOR, $default);
      }
      // Plug for library
      Iterators[NAME] = $default;
      Iterators[TAG] = returnThis;
      if (DEFAULT) {
        methods = {
          values: DEF_VALUES ? $default : getMethod(VALUES),
          keys: IS_SET ? $default : getMethod(KEYS),
          entries: $entries
        };
        if (FORCED) for (key in methods) {
          if (!(key in proto)) redefine(proto, key, methods[key]);
        } else $export($export.P + $export.F * (BUGGY || VALUES_BUG), NAME, methods);
      }
      return methods;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_iter-detect.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_iter-detect.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var ITERATOR = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('iterator');
    var SAFE_CLOSING = false;
    
    try {
      var riter = [7][ITERATOR]();
      riter['return'] = function () { SAFE_CLOSING = true; };
      // eslint-disable-next-line no-throw-literal
      Array.from(riter, function () { throw 2; });
    } catch (e) { /* empty */ }
    
    module.exports = function (exec, skipClosing) {
      if (!skipClosing && !SAFE_CLOSING) return false;
      var safe = false;
      try {
        var arr = [7];
        var iter = arr[ITERATOR]();
        iter.next = function () { return { done: safe = true }; };
        arr[ITERATOR] = function () { return iter; };
        exec(arr);
      } catch (e) { /* empty */ }
      return safe;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_iter-step.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_iter-step.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = function (done, value) {
      return { value: value, done: !!done };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_iterators.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_iterators.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = {};
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_library.js":
    /*!**************************************************!*\
      !*** ./node_modules/core-js/modules/_library.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = false;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_math-expm1.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_math-expm1.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // 20.2.2.14 Math.expm1(x)
    var $expm1 = Math.expm1;
    module.exports = (!$expm1
      // Old FF bug
      || $expm1(10) > 22025.465794806719 || $expm1(10) < 22025.4657948067165168
      // Tor Browser bug
      || $expm1(-2e-17) != -2e-17
    ) ? function expm1(x) {
      return (x = +x) == 0 ? x : x > -1e-6 && x < 1e-6 ? x + x * x / 2 : Math.exp(x) - 1;
    } : $expm1;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_math-fround.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_math-fround.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.16 Math.fround(x)
    var sign = __webpack_require__(/*! ./_math-sign */ "./node_modules/core-js/modules/_math-sign.js");
    var pow = Math.pow;
    var EPSILON = pow(2, -52);
    var EPSILON32 = pow(2, -23);
    var MAX32 = pow(2, 127) * (2 - EPSILON32);
    var MIN32 = pow(2, -126);
    
    var roundTiesToEven = function (n) {
      return n + 1 / EPSILON - 1 / EPSILON;
    };
    
    module.exports = Math.fround || function fround(x) {
      var $abs = Math.abs(x);
      var $sign = sign(x);
      var a, result;
      if ($abs < MIN32) return $sign * roundTiesToEven($abs / MIN32 / EPSILON32) * MIN32 * EPSILON32;
      a = (1 + EPSILON32 / EPSILON) * $abs;
      result = a - (a - $abs);
      // eslint-disable-next-line no-self-compare
      if (result > MAX32 || result != result) return $sign * Infinity;
      return $sign * result;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_math-log1p.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_math-log1p.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // 20.2.2.20 Math.log1p(x)
    module.exports = Math.log1p || function log1p(x) {
      return (x = +x) > -1e-8 && x < 1e-8 ? x - x * x / 2 : Math.log(1 + x);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_math-scale.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_math-scale.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // https://rwaldron.github.io/proposal-math-extensions/
    module.exports = Math.scale || function scale(x, inLow, inHigh, outLow, outHigh) {
      if (
        arguments.length === 0
          // eslint-disable-next-line no-self-compare
          || x != x
          // eslint-disable-next-line no-self-compare
          || inLow != inLow
          // eslint-disable-next-line no-self-compare
          || inHigh != inHigh
          // eslint-disable-next-line no-self-compare
          || outLow != outLow
          // eslint-disable-next-line no-self-compare
          || outHigh != outHigh
      ) return NaN;
      if (x === Infinity || x === -Infinity) return x;
      return (x - inLow) * (outHigh - outLow) / (inHigh - inLow) + outLow;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_math-sign.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_math-sign.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // 20.2.2.28 Math.sign(x)
    module.exports = Math.sign || function sign(x) {
      // eslint-disable-next-line no-self-compare
      return (x = +x) == 0 || x != x ? x : x < 0 ? -1 : 1;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_meta.js":
    /*!***********************************************!*\
      !*** ./node_modules/core-js/modules/_meta.js ***!
      \***********************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var META = __webpack_require__(/*! ./_uid */ "./node_modules/core-js/modules/_uid.js")('meta');
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var setDesc = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f;
    var id = 0;
    var isExtensible = Object.isExtensible || function () {
      return true;
    };
    var FREEZE = !__webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      return isExtensible(Object.preventExtensions({}));
    });
    var setMeta = function (it) {
      setDesc(it, META, { value: {
        i: 'O' + ++id, // object ID
        w: {}          // weak collections IDs
      } });
    };
    var fastKey = function (it, create) {
      // return primitive with prefix
      if (!isObject(it)) return typeof it == 'symbol' ? it : (typeof it == 'string' ? 'S' : 'P') + it;
      if (!has(it, META)) {
        // can't set metadata to uncaught frozen object
        if (!isExtensible(it)) return 'F';
        // not necessary to add metadata
        if (!create) return 'E';
        // add missing metadata
        setMeta(it);
      // return object ID
      } return it[META].i;
    };
    var getWeak = function (it, create) {
      if (!has(it, META)) {
        // can't set metadata to uncaught frozen object
        if (!isExtensible(it)) return true;
        // not necessary to add metadata
        if (!create) return false;
        // add missing metadata
        setMeta(it);
      // return hash weak collections IDs
      } return it[META].w;
    };
    // add metadata on freeze-family methods calling
    var onFreeze = function (it) {
      if (FREEZE && meta.NEED && isExtensible(it) && !has(it, META)) setMeta(it);
      return it;
    };
    var meta = module.exports = {
      KEY: META,
      NEED: false,
      fastKey: fastKey,
      getWeak: getWeak,
      onFreeze: onFreeze
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_metadata.js":
    /*!***************************************************!*\
      !*** ./node_modules/core-js/modules/_metadata.js ***!
      \***************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var Map = __webpack_require__(/*! ./es6.map */ "./node_modules/core-js/modules/es6.map.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var shared = __webpack_require__(/*! ./_shared */ "./node_modules/core-js/modules/_shared.js")('metadata');
    var store = shared.store || (shared.store = new (__webpack_require__(/*! ./es6.weak-map */ "./node_modules/core-js/modules/es6.weak-map.js"))());
    
    var getOrCreateMetadataMap = function (target, targetKey, create) {
      var targetMetadata = store.get(target);
      if (!targetMetadata) {
        if (!create) return undefined;
        store.set(target, targetMetadata = new Map());
      }
      var keyMetadata = targetMetadata.get(targetKey);
      if (!keyMetadata) {
        if (!create) return undefined;
        targetMetadata.set(targetKey, keyMetadata = new Map());
      } return keyMetadata;
    };
    var ordinaryHasOwnMetadata = function (MetadataKey, O, P) {
      var metadataMap = getOrCreateMetadataMap(O, P, false);
      return metadataMap === undefined ? false : metadataMap.has(MetadataKey);
    };
    var ordinaryGetOwnMetadata = function (MetadataKey, O, P) {
      var metadataMap = getOrCreateMetadataMap(O, P, false);
      return metadataMap === undefined ? undefined : metadataMap.get(MetadataKey);
    };
    var ordinaryDefineOwnMetadata = function (MetadataKey, MetadataValue, O, P) {
      getOrCreateMetadataMap(O, P, true).set(MetadataKey, MetadataValue);
    };
    var ordinaryOwnMetadataKeys = function (target, targetKey) {
      var metadataMap = getOrCreateMetadataMap(target, targetKey, false);
      var keys = [];
      if (metadataMap) metadataMap.forEach(function (_, key) { keys.push(key); });
      return keys;
    };
    var toMetaKey = function (it) {
      return it === undefined || typeof it == 'symbol' ? it : String(it);
    };
    var exp = function (O) {
      $export($export.S, 'Reflect', O);
    };
    
    module.exports = {
      store: store,
      map: getOrCreateMetadataMap,
      has: ordinaryHasOwnMetadata,
      get: ordinaryGetOwnMetadata,
      set: ordinaryDefineOwnMetadata,
      keys: ordinaryOwnMetadataKeys,
      key: toMetaKey,
      exp: exp
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_microtask.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_microtask.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var macrotask = __webpack_require__(/*! ./_task */ "./node_modules/core-js/modules/_task.js").set;
    var Observer = global.MutationObserver || global.WebKitMutationObserver;
    var process = global.process;
    var Promise = global.Promise;
    var isNode = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js")(process) == 'process';
    
    module.exports = function () {
      var head, last, notify;
    
      var flush = function () {
        var parent, fn;
        if (isNode && (parent = process.domain)) parent.exit();
        while (head) {
          fn = head.fn;
          head = head.next;
          try {
            fn();
          } catch (e) {
            if (head) notify();
            else last = undefined;
            throw e;
          }
        } last = undefined;
        if (parent) parent.enter();
      };
    
      // Node.js
      if (isNode) {
        notify = function () {
          process.nextTick(flush);
        };
      // browsers with MutationObserver, except iOS Safari - https://github.com/zloirock/core-js/issues/339
      } else if (Observer && !(global.navigator && global.navigator.standalone)) {
        var toggle = true;
        var node = document.createTextNode('');
        new Observer(flush).observe(node, { characterData: true }); // eslint-disable-line no-new
        notify = function () {
          node.data = toggle = !toggle;
        };
      // environments with maybe non-completely correct, but existent Promise
      } else if (Promise && Promise.resolve) {
        // Promise.resolve without an argument throws an error in LG WebOS 2
        var promise = Promise.resolve(undefined);
        notify = function () {
          promise.then(flush);
        };
      // for other environments - macrotask based on:
      // - setImmediate
      // - MessageChannel
      // - window.postMessag
      // - onreadystatechange
      // - setTimeout
      } else {
        notify = function () {
          // strange IE + webpack dev server bug - use .call(global)
          macrotask.call(global, flush);
        };
      }
    
      return function (fn) {
        var task = { fn: fn, next: undefined };
        if (last) last.next = task;
        if (!head) {
          head = task;
          notify();
        } last = task;
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_new-promise-capability.js":
    /*!*****************************************************************!*\
      !*** ./node_modules/core-js/modules/_new-promise-capability.js ***!
      \*****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 25.4.1.5 NewPromiseCapability(C)
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    
    function PromiseCapability(C) {
      var resolve, reject;
      this.promise = new C(function ($$resolve, $$reject) {
        if (resolve !== undefined || reject !== undefined) throw TypeError('Bad Promise constructor');
        resolve = $$resolve;
        reject = $$reject;
      });
      this.resolve = aFunction(resolve);
      this.reject = aFunction(reject);
    }
    
    module.exports.f = function (C) {
      return new PromiseCapability(C);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-assign.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/_object-assign.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 19.1.2.1 Object.assign(target, source, ...)
    var getKeys = __webpack_require__(/*! ./_object-keys */ "./node_modules/core-js/modules/_object-keys.js");
    var gOPS = __webpack_require__(/*! ./_object-gops */ "./node_modules/core-js/modules/_object-gops.js");
    var pIE = __webpack_require__(/*! ./_object-pie */ "./node_modules/core-js/modules/_object-pie.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var IObject = __webpack_require__(/*! ./_iobject */ "./node_modules/core-js/modules/_iobject.js");
    var $assign = Object.assign;
    
    // should work with symbols and should have deterministic property order (V8 bug)
    module.exports = !$assign || __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      var A = {};
      var B = {};
      // eslint-disable-next-line no-undef
      var S = Symbol();
      var K = 'abcdefghijklmnopqrst';
      A[S] = 7;
      K.split('').forEach(function (k) { B[k] = k; });
      return $assign({}, A)[S] != 7 || Object.keys($assign({}, B)).join('') != K;
    }) ? function assign(target, source) { // eslint-disable-line no-unused-vars
      var T = toObject(target);
      var aLen = arguments.length;
      var index = 1;
      var getSymbols = gOPS.f;
      var isEnum = pIE.f;
      while (aLen > index) {
        var S = IObject(arguments[index++]);
        var keys = getSymbols ? getKeys(S).concat(getSymbols(S)) : getKeys(S);
        var length = keys.length;
        var j = 0;
        var key;
        while (length > j) if (isEnum.call(S, key = keys[j++])) T[key] = S[key];
      } return T;
    } : $assign;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-create.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/_object-create.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var dPs = __webpack_require__(/*! ./_object-dps */ "./node_modules/core-js/modules/_object-dps.js");
    var enumBugKeys = __webpack_require__(/*! ./_enum-bug-keys */ "./node_modules/core-js/modules/_enum-bug-keys.js");
    var IE_PROTO = __webpack_require__(/*! ./_shared-key */ "./node_modules/core-js/modules/_shared-key.js")('IE_PROTO');
    var Empty = function () { /* empty */ };
    var PROTOTYPE = 'prototype';
    
    // Create object with fake `null` prototype: use iframe Object with cleared prototype
    var createDict = function () {
      // Thrash, waste and sodomy: IE GC bug
      var iframe = __webpack_require__(/*! ./_dom-create */ "./node_modules/core-js/modules/_dom-create.js")('iframe');
      var i = enumBugKeys.length;
      var lt = '<';
      var gt = '>';
      var iframeDocument;
      iframe.style.display = 'none';
      __webpack_require__(/*! ./_html */ "./node_modules/core-js/modules/_html.js").appendChild(iframe);
      iframe.src = 'javascript:'; // eslint-disable-line no-script-url
      // createDict = iframe.contentWindow.Object;
      // html.removeChild(iframe);
      iframeDocument = iframe.contentWindow.document;
      iframeDocument.open();
      iframeDocument.write(lt + 'script' + gt + 'document.F=Object' + lt + '/script' + gt);
      iframeDocument.close();
      createDict = iframeDocument.F;
      while (i--) delete createDict[PROTOTYPE][enumBugKeys[i]];
      return createDict();
    };
    
    module.exports = Object.create || function create(O, Properties) {
      var result;
      if (O !== null) {
        Empty[PROTOTYPE] = anObject(O);
        result = new Empty();
        Empty[PROTOTYPE] = null;
        // add "__proto__" for Object.getPrototypeOf polyfill
        result[IE_PROTO] = O;
      } else result = createDict();
      return Properties === undefined ? result : dPs(result, Properties);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-dp.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_object-dp.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var IE8_DOM_DEFINE = __webpack_require__(/*! ./_ie8-dom-define */ "./node_modules/core-js/modules/_ie8-dom-define.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    var dP = Object.defineProperty;
    
    exports.f = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") ? Object.defineProperty : function defineProperty(O, P, Attributes) {
      anObject(O);
      P = toPrimitive(P, true);
      anObject(Attributes);
      if (IE8_DOM_DEFINE) try {
        return dP(O, P, Attributes);
      } catch (e) { /* empty */ }
      if ('get' in Attributes || 'set' in Attributes) throw TypeError('Accessors not supported!');
      if ('value' in Attributes) O[P] = Attributes.value;
      return O;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-dps.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_object-dps.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var getKeys = __webpack_require__(/*! ./_object-keys */ "./node_modules/core-js/modules/_object-keys.js");
    
    module.exports = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") ? Object.defineProperties : function defineProperties(O, Properties) {
      anObject(O);
      var keys = getKeys(Properties);
      var length = keys.length;
      var i = 0;
      var P;
      while (length > i) dP.f(O, P = keys[i++], Properties[P]);
      return O;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-forced-pam.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/_object-forced-pam.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // Forced replacement prototype accessors methods
    module.exports = __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js") || !__webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      var K = Math.random();
      // In FF throws only define methods
      // eslint-disable-next-line no-undef, no-useless-call
      __defineSetter__.call(null, K, function () { /* empty */ });
      delete __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js")[K];
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-gopd.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_object-gopd.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var pIE = __webpack_require__(/*! ./_object-pie */ "./node_modules/core-js/modules/_object-pie.js");
    var createDesc = __webpack_require__(/*! ./_property-desc */ "./node_modules/core-js/modules/_property-desc.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var IE8_DOM_DEFINE = __webpack_require__(/*! ./_ie8-dom-define */ "./node_modules/core-js/modules/_ie8-dom-define.js");
    var gOPD = Object.getOwnPropertyDescriptor;
    
    exports.f = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") ? gOPD : function getOwnPropertyDescriptor(O, P) {
      O = toIObject(O);
      P = toPrimitive(P, true);
      if (IE8_DOM_DEFINE) try {
        return gOPD(O, P);
      } catch (e) { /* empty */ }
      if (has(O, P)) return createDesc(!pIE.f.call(O, P), O[P]);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-gopn-ext.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/_object-gopn-ext.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // fallback for IE11 buggy Object.getOwnPropertyNames with iframe and window
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var gOPN = __webpack_require__(/*! ./_object-gopn */ "./node_modules/core-js/modules/_object-gopn.js").f;
    var toString = {}.toString;
    
    var windowNames = typeof window == 'object' && window && Object.getOwnPropertyNames
      ? Object.getOwnPropertyNames(window) : [];
    
    var getWindowNames = function (it) {
      try {
        return gOPN(it);
      } catch (e) {
        return windowNames.slice();
      }
    };
    
    module.exports.f = function getOwnPropertyNames(it) {
      return windowNames && toString.call(it) == '[object Window]' ? getWindowNames(it) : gOPN(toIObject(it));
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-gopn.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_object-gopn.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.7 / 15.2.3.4 Object.getOwnPropertyNames(O)
    var $keys = __webpack_require__(/*! ./_object-keys-internal */ "./node_modules/core-js/modules/_object-keys-internal.js");
    var hiddenKeys = __webpack_require__(/*! ./_enum-bug-keys */ "./node_modules/core-js/modules/_enum-bug-keys.js").concat('length', 'prototype');
    
    exports.f = Object.getOwnPropertyNames || function getOwnPropertyNames(O) {
      return $keys(O, hiddenKeys);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-gops.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_object-gops.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    exports.f = Object.getOwnPropertySymbols;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-gpo.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_object-gpo.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.9 / 15.2.3.2 Object.getPrototypeOf(O)
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var IE_PROTO = __webpack_require__(/*! ./_shared-key */ "./node_modules/core-js/modules/_shared-key.js")('IE_PROTO');
    var ObjectProto = Object.prototype;
    
    module.exports = Object.getPrototypeOf || function (O) {
      O = toObject(O);
      if (has(O, IE_PROTO)) return O[IE_PROTO];
      if (typeof O.constructor == 'function' && O instanceof O.constructor) {
        return O.constructor.prototype;
      } return O instanceof Object ? ObjectProto : null;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-keys-internal.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/_object-keys-internal.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var arrayIndexOf = __webpack_require__(/*! ./_array-includes */ "./node_modules/core-js/modules/_array-includes.js")(false);
    var IE_PROTO = __webpack_require__(/*! ./_shared-key */ "./node_modules/core-js/modules/_shared-key.js")('IE_PROTO');
    
    module.exports = function (object, names) {
      var O = toIObject(object);
      var i = 0;
      var result = [];
      var key;
      for (key in O) if (key != IE_PROTO) has(O, key) && result.push(key);
      // Don't enum bug & hidden keys
      while (names.length > i) if (has(O, key = names[i++])) {
        ~arrayIndexOf(result, key) || result.push(key);
      }
      return result;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-keys.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_object-keys.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.14 / 15.2.3.14 Object.keys(O)
    var $keys = __webpack_require__(/*! ./_object-keys-internal */ "./node_modules/core-js/modules/_object-keys-internal.js");
    var enumBugKeys = __webpack_require__(/*! ./_enum-bug-keys */ "./node_modules/core-js/modules/_enum-bug-keys.js");
    
    module.exports = Object.keys || function keys(O) {
      return $keys(O, enumBugKeys);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-pie.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_object-pie.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    exports.f = {}.propertyIsEnumerable;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-sap.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_object-sap.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // most Object methods by ES6 should accept primitives
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var core = __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    module.exports = function (KEY, exec) {
      var fn = (core.Object || {})[KEY] || Object[KEY];
      var exp = {};
      exp[KEY] = exec(fn);
      $export($export.S + $export.F * fails(function () { fn(1); }), 'Object', exp);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_object-to-array.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/_object-to-array.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var getKeys = __webpack_require__(/*! ./_object-keys */ "./node_modules/core-js/modules/_object-keys.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var isEnum = __webpack_require__(/*! ./_object-pie */ "./node_modules/core-js/modules/_object-pie.js").f;
    module.exports = function (isEntries) {
      return function (it) {
        var O = toIObject(it);
        var keys = getKeys(O);
        var length = keys.length;
        var i = 0;
        var result = [];
        var key;
        while (length > i) if (isEnum.call(O, key = keys[i++])) {
          result.push(isEntries ? [key, O[key]] : O[key]);
        } return result;
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_own-keys.js":
    /*!***************************************************!*\
      !*** ./node_modules/core-js/modules/_own-keys.js ***!
      \***************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // all object keys, includes non-enumerable and symbols
    var gOPN = __webpack_require__(/*! ./_object-gopn */ "./node_modules/core-js/modules/_object-gopn.js");
    var gOPS = __webpack_require__(/*! ./_object-gops */ "./node_modules/core-js/modules/_object-gops.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var Reflect = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").Reflect;
    module.exports = Reflect && Reflect.ownKeys || function ownKeys(it) {
      var keys = gOPN.f(anObject(it));
      var getSymbols = gOPS.f;
      return getSymbols ? keys.concat(getSymbols(it)) : keys;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_parse-float.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_parse-float.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $parseFloat = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").parseFloat;
    var $trim = __webpack_require__(/*! ./_string-trim */ "./node_modules/core-js/modules/_string-trim.js").trim;
    
    module.exports = 1 / $parseFloat(__webpack_require__(/*! ./_string-ws */ "./node_modules/core-js/modules/_string-ws.js") + '-0') !== -Infinity ? function parseFloat(str) {
      var string = $trim(String(str), 3);
      var result = $parseFloat(string);
      return result === 0 && string.charAt(0) == '-' ? -0 : result;
    } : $parseFloat;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_parse-int.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_parse-int.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $parseInt = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").parseInt;
    var $trim = __webpack_require__(/*! ./_string-trim */ "./node_modules/core-js/modules/_string-trim.js").trim;
    var ws = __webpack_require__(/*! ./_string-ws */ "./node_modules/core-js/modules/_string-ws.js");
    var hex = /^[-+]?0[xX]/;
    
    module.exports = $parseInt(ws + '08') !== 8 || $parseInt(ws + '0x16') !== 22 ? function parseInt(str, radix) {
      var string = $trim(String(str), 3);
      return $parseInt(string, (radix >>> 0) || (hex.test(string) ? 16 : 10));
    } : $parseInt;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_perform.js":
    /*!**************************************************!*\
      !*** ./node_modules/core-js/modules/_perform.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = function (exec) {
      try {
        return { e: false, v: exec() };
      } catch (e) {
        return { e: true, v: e };
      }
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_promise-resolve.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/_promise-resolve.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var newPromiseCapability = __webpack_require__(/*! ./_new-promise-capability */ "./node_modules/core-js/modules/_new-promise-capability.js");
    
    module.exports = function (C, x) {
      anObject(C);
      if (isObject(x) && x.constructor === C) return x;
      var promiseCapability = newPromiseCapability.f(C);
      var resolve = promiseCapability.resolve;
      resolve(x);
      return promiseCapability.promise;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_property-desc.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/_property-desc.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = function (bitmap, value) {
      return {
        enumerable: !(bitmap & 1),
        configurable: !(bitmap & 2),
        writable: !(bitmap & 4),
        value: value
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_redefine-all.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/_redefine-all.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var redefine = __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js");
    module.exports = function (target, src, safe) {
      for (var key in src) redefine(target, key, src[key], safe);
      return target;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_redefine.js":
    /*!***************************************************!*\
      !*** ./node_modules/core-js/modules/_redefine.js ***!
      \***************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var SRC = __webpack_require__(/*! ./_uid */ "./node_modules/core-js/modules/_uid.js")('src');
    var $toString = __webpack_require__(/*! ./_function-to-string */ "./node_modules/core-js/modules/_function-to-string.js");
    var TO_STRING = 'toString';
    var TPL = ('' + $toString).split(TO_STRING);
    
    __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js").inspectSource = function (it) {
      return $toString.call(it);
    };
    
    (module.exports = function (O, key, val, safe) {
      var isFunction = typeof val == 'function';
      if (isFunction) has(val, 'name') || hide(val, 'name', key);
      if (O[key] === val) return;
      if (isFunction) has(val, SRC) || hide(val, SRC, O[key] ? '' + O[key] : TPL.join(String(key)));
      if (O === global) {
        O[key] = val;
      } else if (!safe) {
        delete O[key];
        hide(O, key, val);
      } else if (O[key]) {
        O[key] = val;
      } else {
        hide(O, key, val);
      }
    // add fake Function#toString for correct work wrapped methods / constructors with methods like LoDash isNative
    })(Function.prototype, TO_STRING, function toString() {
      return typeof this == 'function' && this[SRC] || $toString.call(this);
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_regexp-exec-abstract.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/_regexp-exec-abstract.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    var classof = __webpack_require__(/*! ./_classof */ "./node_modules/core-js/modules/_classof.js");
    var builtinExec = RegExp.prototype.exec;
    
     // `RegExpExec` abstract operation
    // https://tc39.github.io/ecma262/#sec-regexpexec
    module.exports = function (R, S) {
      var exec = R.exec;
      if (typeof exec === 'function') {
        var result = exec.call(R, S);
        if (typeof result !== 'object') {
          throw new TypeError('RegExp exec method returned something other than an Object or null');
        }
        return result;
      }
      if (classof(R) !== 'RegExp') {
        throw new TypeError('RegExp#exec called on incompatible receiver');
      }
      return builtinExec.call(R, S);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_regexp-exec.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_regexp-exec.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    var regexpFlags = __webpack_require__(/*! ./_flags */ "./node_modules/core-js/modules/_flags.js");
    
    var nativeExec = RegExp.prototype.exec;
    // This always refers to the native implementation, because the
    // String#replace polyfill uses ./fix-regexp-well-known-symbol-logic.js,
    // which loads this file before patching the method.
    var nativeReplace = String.prototype.replace;
    
    var patchedExec = nativeExec;
    
    var LAST_INDEX = 'lastIndex';
    
    var UPDATES_LAST_INDEX_WRONG = (function () {
      var re1 = /a/,
          re2 = /b*/g;
      nativeExec.call(re1, 'a');
      nativeExec.call(re2, 'a');
      return re1[LAST_INDEX] !== 0 || re2[LAST_INDEX] !== 0;
    })();
    
    // nonparticipating capturing group, copied from es5-shim's String#split patch.
    var NPCG_INCLUDED = /()??/.exec('')[1] !== undefined;
    
    var PATCH = UPDATES_LAST_INDEX_WRONG || NPCG_INCLUDED;
    
    if (PATCH) {
      patchedExec = function exec(str) {
        var re = this;
        var lastIndex, reCopy, match, i;
    
        if (NPCG_INCLUDED) {
          reCopy = new RegExp('^' + re.source + '$(?!\\s)', regexpFlags.call(re));
        }
        if (UPDATES_LAST_INDEX_WRONG) lastIndex = re[LAST_INDEX];
    
        match = nativeExec.call(re, str);
    
        if (UPDATES_LAST_INDEX_WRONG && match) {
          re[LAST_INDEX] = re.global ? match.index + match[0].length : lastIndex;
        }
        if (NPCG_INCLUDED && match && match.length > 1) {
          // Fix browsers whose `exec` methods don't consistently return `undefined`
          // for NPCG, like IE8. NOTE: This doesn' work for /(.?)?/
          // eslint-disable-next-line no-loop-func
          nativeReplace.call(match[0], reCopy, function () {
            for (i = 1; i < arguments.length - 2; i++) {
              if (arguments[i] === undefined) match[i] = undefined;
            }
          });
        }
    
        return match;
      };
    }
    
    module.exports = patchedExec;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_replacer.js":
    /*!***************************************************!*\
      !*** ./node_modules/core-js/modules/_replacer.js ***!
      \***************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = function (regExp, replace) {
      var replacer = replace === Object(replace) ? function (part) {
        return replace[part];
      } : replace;
      return function (it) {
        return String(it).replace(regExp, replacer);
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_same-value.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_same-value.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // 7.2.9 SameValue(x, y)
    module.exports = Object.is || function is(x, y) {
      // eslint-disable-next-line no-self-compare
      return x === y ? x !== 0 || 1 / x === 1 / y : x != x && y != y;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_set-collection-from.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/_set-collection-from.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://tc39.github.io/proposal-setmap-offrom/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var forOf = __webpack_require__(/*! ./_for-of */ "./node_modules/core-js/modules/_for-of.js");
    
    module.exports = function (COLLECTION) {
      $export($export.S, COLLECTION, { from: function from(source /* , mapFn, thisArg */) {
        var mapFn = arguments[1];
        var mapping, A, n, cb;
        aFunction(this);
        mapping = mapFn !== undefined;
        if (mapping) aFunction(mapFn);
        if (source == undefined) return new this();
        A = [];
        if (mapping) {
          n = 0;
          cb = ctx(mapFn, arguments[2], 2);
          forOf(source, false, function (nextItem) {
            A.push(cb(nextItem, n++));
          });
        } else {
          forOf(source, false, A.push, A);
        }
        return new this(A);
      } });
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_set-collection-of.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/_set-collection-of.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://tc39.github.io/proposal-setmap-offrom/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    module.exports = function (COLLECTION) {
      $export($export.S, COLLECTION, { of: function of() {
        var length = arguments.length;
        var A = new Array(length);
        while (length--) A[length] = arguments[length];
        return new this(A);
      } });
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_set-proto.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_set-proto.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // Works with __proto__ only. Old v8 can't work with null proto objects.
    /* eslint-disable no-proto */
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var check = function (O, proto) {
      anObject(O);
      if (!isObject(proto) && proto !== null) throw TypeError(proto + ": can't set as prototype!");
    };
    module.exports = {
      set: Object.setPrototypeOf || ('__proto__' in {} ? // eslint-disable-line
        function (test, buggy, set) {
          try {
            set = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js")(Function.call, __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js").f(Object.prototype, '__proto__').set, 2);
            set(test, []);
            buggy = !(test instanceof Array);
          } catch (e) { buggy = true; }
          return function setPrototypeOf(O, proto) {
            check(O, proto);
            if (buggy) O.__proto__ = proto;
            else set(O, proto);
            return O;
          };
        }({}, false) : undefined),
      check: check
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_set-species.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_set-species.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    var DESCRIPTORS = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js");
    var SPECIES = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('species');
    
    module.exports = function (KEY) {
      var C = global[KEY];
      if (DESCRIPTORS && C && !C[SPECIES]) dP.f(C, SPECIES, {
        configurable: true,
        get: function () { return this; }
      });
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_set-to-string-tag.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/_set-to-string-tag.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var def = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f;
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var TAG = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('toStringTag');
    
    module.exports = function (it, tag, stat) {
      if (it && !has(it = stat ? it : it.prototype, TAG)) def(it, TAG, { configurable: true, value: tag });
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_shared-key.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_shared-key.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var shared = __webpack_require__(/*! ./_shared */ "./node_modules/core-js/modules/_shared.js")('keys');
    var uid = __webpack_require__(/*! ./_uid */ "./node_modules/core-js/modules/_uid.js");
    module.exports = function (key) {
      return shared[key] || (shared[key] = uid(key));
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_shared.js":
    /*!*************************************************!*\
      !*** ./node_modules/core-js/modules/_shared.js ***!
      \*************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var core = __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js");
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var SHARED = '__core-js_shared__';
    var store = global[SHARED] || (global[SHARED] = {});
    
    (module.exports = function (key, value) {
      return store[key] || (store[key] = value !== undefined ? value : {});
    })('versions', []).push({
      version: core.version,
      mode: __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js") ? 'pure' : 'global',
      copyright: '© 2019 Denis Pushkarev (zloirock.ru)'
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_species-constructor.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/_species-constructor.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 7.3.20 SpeciesConstructor(O, defaultConstructor)
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var SPECIES = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('species');
    module.exports = function (O, D) {
      var C = anObject(O).constructor;
      var S;
      return C === undefined || (S = anObject(C)[SPECIES]) == undefined ? D : aFunction(S);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_strict-method.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/_strict-method.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    
    module.exports = function (method, arg) {
      return !!method && fails(function () {
        // eslint-disable-next-line no-useless-call
        arg ? method.call(null, function () { /* empty */ }, 1) : method.call(null);
      });
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_string-at.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_string-at.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    // true  -> String#at
    // false -> String#codePointAt
    module.exports = function (TO_STRING) {
      return function (that, pos) {
        var s = String(defined(that));
        var i = toInteger(pos);
        var l = s.length;
        var a, b;
        if (i < 0 || i >= l) return TO_STRING ? '' : undefined;
        a = s.charCodeAt(i);
        return a < 0xd800 || a > 0xdbff || i + 1 === l || (b = s.charCodeAt(i + 1)) < 0xdc00 || b > 0xdfff
          ? TO_STRING ? s.charAt(i) : a
          : TO_STRING ? s.slice(i, i + 2) : (a - 0xd800 << 10) + (b - 0xdc00) + 0x10000;
      };
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_string-context.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/_string-context.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // helper for String#{startsWith, endsWith, includes}
    var isRegExp = __webpack_require__(/*! ./_is-regexp */ "./node_modules/core-js/modules/_is-regexp.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    
    module.exports = function (that, searchString, NAME) {
      if (isRegExp(searchString)) throw TypeError('String#' + NAME + " doesn't accept regex!");
      return String(defined(that));
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_string-html.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_string-html.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    var quot = /"/g;
    // B.2.3.2.1 CreateHTML(string, tag, attribute, value)
    var createHTML = function (string, tag, attribute, value) {
      var S = String(defined(string));
      var p1 = '<' + tag;
      if (attribute !== '') p1 += ' ' + attribute + '="' + String(value).replace(quot, '"') + '"';
      return p1 + '>' + S + '';
    };
    module.exports = function (NAME, exec) {
      var O = {};
      O[NAME] = exec(createHTML);
      $export($export.P + $export.F * fails(function () {
        var test = ''[NAME]('"');
        return test !== test.toLowerCase() || test.split('"').length > 3;
      }), 'String', O);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_string-pad.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_string-pad.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/tc39/proposal-string-pad-start-end
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var repeat = __webpack_require__(/*! ./_string-repeat */ "./node_modules/core-js/modules/_string-repeat.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    
    module.exports = function (that, maxLength, fillString, left) {
      var S = String(defined(that));
      var stringLength = S.length;
      var fillStr = fillString === undefined ? ' ' : String(fillString);
      var intMaxLength = toLength(maxLength);
      if (intMaxLength <= stringLength || fillStr == '') return S;
      var fillLen = intMaxLength - stringLength;
      var stringFiller = repeat.call(fillStr, Math.ceil(fillLen / fillStr.length));
      if (stringFiller.length > fillLen) stringFiller = stringFiller.slice(0, fillLen);
      return left ? stringFiller + S : S + stringFiller;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_string-repeat.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/_string-repeat.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    
    module.exports = function repeat(count) {
      var str = String(defined(this));
      var res = '';
      var n = toInteger(count);
      if (n < 0 || n == Infinity) throw RangeError("Count can't be negative");
      for (;n > 0; (n >>>= 1) && (str += str)) if (n & 1) res += str;
      return res;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_string-trim.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_string-trim.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var spaces = __webpack_require__(/*! ./_string-ws */ "./node_modules/core-js/modules/_string-ws.js");
    var space = '[' + spaces + ']';
    var non = '\u200b\u0085';
    var ltrim = RegExp('^' + space + space + '*');
    var rtrim = RegExp(space + space + '*$');
    
    var exporter = function (KEY, exec, ALIAS) {
      var exp = {};
      var FORCE = fails(function () {
        return !!spaces[KEY]() || non[KEY]() != non;
      });
      var fn = exp[KEY] = FORCE ? exec(trim) : spaces[KEY];
      if (ALIAS) exp[ALIAS] = fn;
      $export($export.P + $export.F * FORCE, 'String', exp);
    };
    
    // 1 -> String#trimLeft
    // 2 -> String#trimRight
    // 3 -> String#trim
    var trim = exporter.trim = function (string, TYPE) {
      string = String(defined(string));
      if (TYPE & 1) string = string.replace(ltrim, '');
      if (TYPE & 2) string = string.replace(rtrim, '');
      return string;
    };
    
    module.exports = exporter;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_string-ws.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_string-ws.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    module.exports = '\x09\x0A\x0B\x0C\x0D\x20\xA0\u1680\u180E\u2000\u2001\u2002\u2003' +
      '\u2004\u2005\u2006\u2007\u2008\u2009\u200A\u202F\u205F\u3000\u2028\u2029\uFEFF';
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_task.js":
    /*!***********************************************!*\
      !*** ./node_modules/core-js/modules/_task.js ***!
      \***********************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var invoke = __webpack_require__(/*! ./_invoke */ "./node_modules/core-js/modules/_invoke.js");
    var html = __webpack_require__(/*! ./_html */ "./node_modules/core-js/modules/_html.js");
    var cel = __webpack_require__(/*! ./_dom-create */ "./node_modules/core-js/modules/_dom-create.js");
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var process = global.process;
    var setTask = global.setImmediate;
    var clearTask = global.clearImmediate;
    var MessageChannel = global.MessageChannel;
    var Dispatch = global.Dispatch;
    var counter = 0;
    var queue = {};
    var ONREADYSTATECHANGE = 'onreadystatechange';
    var defer, channel, port;
    var run = function () {
      var id = +this;
      // eslint-disable-next-line no-prototype-builtins
      if (queue.hasOwnProperty(id)) {
        var fn = queue[id];
        delete queue[id];
        fn();
      }
    };
    var listener = function (event) {
      run.call(event.data);
    };
    // Node.js 0.9+ & IE10+ has setImmediate, otherwise:
    if (!setTask || !clearTask) {
      setTask = function setImmediate(fn) {
        var args = [];
        var i = 1;
        while (arguments.length > i) args.push(arguments[i++]);
        queue[++counter] = function () {
          // eslint-disable-next-line no-new-func
          invoke(typeof fn == 'function' ? fn : Function(fn), args);
        };
        defer(counter);
        return counter;
      };
      clearTask = function clearImmediate(id) {
        delete queue[id];
      };
      // Node.js 0.8-
      if (__webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js")(process) == 'process') {
        defer = function (id) {
          process.nextTick(ctx(run, id, 1));
        };
      // Sphere (JS game engine) Dispatch API
      } else if (Dispatch && Dispatch.now) {
        defer = function (id) {
          Dispatch.now(ctx(run, id, 1));
        };
      // Browsers with MessageChannel, includes WebWorkers
      } else if (MessageChannel) {
        channel = new MessageChannel();
        port = channel.port2;
        channel.port1.onmessage = listener;
        defer = ctx(port.postMessage, port, 1);
      // Browsers with postMessage, skip WebWorkers
      // IE8 has postMessage, but it's sync & typeof its postMessage is 'object'
      } else if (global.addEventListener && typeof postMessage == 'function' && !global.importScripts) {
        defer = function (id) {
          global.postMessage(id + '', '*');
        };
        global.addEventListener('message', listener, false);
      // IE8-
      } else if (ONREADYSTATECHANGE in cel('script')) {
        defer = function (id) {
          html.appendChild(cel('script'))[ONREADYSTATECHANGE] = function () {
            html.removeChild(this);
            run.call(id);
          };
        };
      // Rest old browsers
      } else {
        defer = function (id) {
          setTimeout(ctx(run, id, 1), 0);
        };
      }
    }
    module.exports = {
      set: setTask,
      clear: clearTask
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_to-absolute-index.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/_to-absolute-index.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var max = Math.max;
    var min = Math.min;
    module.exports = function (index, length) {
      index = toInteger(index);
      return index < 0 ? max(index + length, 0) : min(index, length);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_to-index.js":
    /*!***************************************************!*\
      !*** ./node_modules/core-js/modules/_to-index.js ***!
      \***************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/ecma262/#sec-toindex
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    module.exports = function (it) {
      if (it === undefined) return 0;
      var number = toInteger(it);
      var length = toLength(number);
      if (number !== length) throw RangeError('Wrong length!');
      return length;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_to-integer.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_to-integer.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // 7.1.4 ToInteger
    var ceil = Math.ceil;
    var floor = Math.floor;
    module.exports = function (it) {
      return isNaN(it = +it) ? 0 : (it > 0 ? floor : ceil)(it);
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_to-iobject.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_to-iobject.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // to indexed object, toObject with fallback for non-array-like ES3 strings
    var IObject = __webpack_require__(/*! ./_iobject */ "./node_modules/core-js/modules/_iobject.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    module.exports = function (it) {
      return IObject(defined(it));
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_to-length.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_to-length.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 7.1.15 ToLength
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var min = Math.min;
    module.exports = function (it) {
      return it > 0 ? min(toInteger(it), 0x1fffffffffffff) : 0; // pow(2, 53) - 1 == 9007199254740991
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_to-object.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/_to-object.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 7.1.13 ToObject(argument)
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    module.exports = function (it) {
      return Object(defined(it));
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_to-primitive.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/_to-primitive.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 7.1.1 ToPrimitive(input [, PreferredType])
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    // instead of the ES6 spec version, we didn't implement @@toPrimitive case
    // and the second argument - flag - preferred type is a string
    module.exports = function (it, S) {
      if (!isObject(it)) return it;
      var fn, val;
      if (S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val;
      if (typeof (fn = it.valueOf) == 'function' && !isObject(val = fn.call(it))) return val;
      if (!S && typeof (fn = it.toString) == 'function' && !isObject(val = fn.call(it))) return val;
      throw TypeError("Can't convert object to primitive value");
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_typed-array.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/_typed-array.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    if (__webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js")) {
      var LIBRARY = __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js");
      var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
      var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
      var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
      var $typed = __webpack_require__(/*! ./_typed */ "./node_modules/core-js/modules/_typed.js");
      var $buffer = __webpack_require__(/*! ./_typed-buffer */ "./node_modules/core-js/modules/_typed-buffer.js");
      var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
      var anInstance = __webpack_require__(/*! ./_an-instance */ "./node_modules/core-js/modules/_an-instance.js");
      var propertyDesc = __webpack_require__(/*! ./_property-desc */ "./node_modules/core-js/modules/_property-desc.js");
      var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
      var redefineAll = __webpack_require__(/*! ./_redefine-all */ "./node_modules/core-js/modules/_redefine-all.js");
      var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
      var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
      var toIndex = __webpack_require__(/*! ./_to-index */ "./node_modules/core-js/modules/_to-index.js");
      var toAbsoluteIndex = __webpack_require__(/*! ./_to-absolute-index */ "./node_modules/core-js/modules/_to-absolute-index.js");
      var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
      var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
      var classof = __webpack_require__(/*! ./_classof */ "./node_modules/core-js/modules/_classof.js");
      var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
      var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
      var isArrayIter = __webpack_require__(/*! ./_is-array-iter */ "./node_modules/core-js/modules/_is-array-iter.js");
      var create = __webpack_require__(/*! ./_object-create */ "./node_modules/core-js/modules/_object-create.js");
      var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
      var gOPN = __webpack_require__(/*! ./_object-gopn */ "./node_modules/core-js/modules/_object-gopn.js").f;
      var getIterFn = __webpack_require__(/*! ./core.get-iterator-method */ "./node_modules/core-js/modules/core.get-iterator-method.js");
      var uid = __webpack_require__(/*! ./_uid */ "./node_modules/core-js/modules/_uid.js");
      var wks = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js");
      var createArrayMethod = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js");
      var createArrayIncludes = __webpack_require__(/*! ./_array-includes */ "./node_modules/core-js/modules/_array-includes.js");
      var speciesConstructor = __webpack_require__(/*! ./_species-constructor */ "./node_modules/core-js/modules/_species-constructor.js");
      var ArrayIterators = __webpack_require__(/*! ./es6.array.iterator */ "./node_modules/core-js/modules/es6.array.iterator.js");
      var Iterators = __webpack_require__(/*! ./_iterators */ "./node_modules/core-js/modules/_iterators.js");
      var $iterDetect = __webpack_require__(/*! ./_iter-detect */ "./node_modules/core-js/modules/_iter-detect.js");
      var setSpecies = __webpack_require__(/*! ./_set-species */ "./node_modules/core-js/modules/_set-species.js");
      var arrayFill = __webpack_require__(/*! ./_array-fill */ "./node_modules/core-js/modules/_array-fill.js");
      var arrayCopyWithin = __webpack_require__(/*! ./_array-copy-within */ "./node_modules/core-js/modules/_array-copy-within.js");
      var $DP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
      var $GOPD = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js");
      var dP = $DP.f;
      var gOPD = $GOPD.f;
      var RangeError = global.RangeError;
      var TypeError = global.TypeError;
      var Uint8Array = global.Uint8Array;
      var ARRAY_BUFFER = 'ArrayBuffer';
      var SHARED_BUFFER = 'Shared' + ARRAY_BUFFER;
      var BYTES_PER_ELEMENT = 'BYTES_PER_ELEMENT';
      var PROTOTYPE = 'prototype';
      var ArrayProto = Array[PROTOTYPE];
      var $ArrayBuffer = $buffer.ArrayBuffer;
      var $DataView = $buffer.DataView;
      var arrayForEach = createArrayMethod(0);
      var arrayFilter = createArrayMethod(2);
      var arraySome = createArrayMethod(3);
      var arrayEvery = createArrayMethod(4);
      var arrayFind = createArrayMethod(5);
      var arrayFindIndex = createArrayMethod(6);
      var arrayIncludes = createArrayIncludes(true);
      var arrayIndexOf = createArrayIncludes(false);
      var arrayValues = ArrayIterators.values;
      var arrayKeys = ArrayIterators.keys;
      var arrayEntries = ArrayIterators.entries;
      var arrayLastIndexOf = ArrayProto.lastIndexOf;
      var arrayReduce = ArrayProto.reduce;
      var arrayReduceRight = ArrayProto.reduceRight;
      var arrayJoin = ArrayProto.join;
      var arraySort = ArrayProto.sort;
      var arraySlice = ArrayProto.slice;
      var arrayToString = ArrayProto.toString;
      var arrayToLocaleString = ArrayProto.toLocaleString;
      var ITERATOR = wks('iterator');
      var TAG = wks('toStringTag');
      var TYPED_CONSTRUCTOR = uid('typed_constructor');
      var DEF_CONSTRUCTOR = uid('def_constructor');
      var ALL_CONSTRUCTORS = $typed.CONSTR;
      var TYPED_ARRAY = $typed.TYPED;
      var VIEW = $typed.VIEW;
      var WRONG_LENGTH = 'Wrong length!';
    
      var $map = createArrayMethod(1, function (O, length) {
        return allocate(speciesConstructor(O, O[DEF_CONSTRUCTOR]), length);
      });
    
      var LITTLE_ENDIAN = fails(function () {
        // eslint-disable-next-line no-undef
        return new Uint8Array(new Uint16Array([1]).buffer)[0] === 1;
      });
    
      var FORCED_SET = !!Uint8Array && !!Uint8Array[PROTOTYPE].set && fails(function () {
        new Uint8Array(1).set({});
      });
    
      var toOffset = function (it, BYTES) {
        var offset = toInteger(it);
        if (offset < 0 || offset % BYTES) throw RangeError('Wrong offset!');
        return offset;
      };
    
      var validate = function (it) {
        if (isObject(it) && TYPED_ARRAY in it) return it;
        throw TypeError(it + ' is not a typed array!');
      };
    
      var allocate = function (C, length) {
        if (!(isObject(C) && TYPED_CONSTRUCTOR in C)) {
          throw TypeError('It is not a typed array constructor!');
        } return new C(length);
      };
    
      var speciesFromList = function (O, list) {
        return fromList(speciesConstructor(O, O[DEF_CONSTRUCTOR]), list);
      };
    
      var fromList = function (C, list) {
        var index = 0;
        var length = list.length;
        var result = allocate(C, length);
        while (length > index) result[index] = list[index++];
        return result;
      };
    
      var addGetter = function (it, key, internal) {
        dP(it, key, { get: function () { return this._d[internal]; } });
      };
    
      var $from = function from(source /* , mapfn, thisArg */) {
        var O = toObject(source);
        var aLen = arguments.length;
        var mapfn = aLen > 1 ? arguments[1] : undefined;
        var mapping = mapfn !== undefined;
        var iterFn = getIterFn(O);
        var i, length, values, result, step, iterator;
        if (iterFn != undefined && !isArrayIter(iterFn)) {
          for (iterator = iterFn.call(O), values = [], i = 0; !(step = iterator.next()).done; i++) {
            values.push(step.value);
          } O = values;
        }
        if (mapping && aLen > 2) mapfn = ctx(mapfn, arguments[2], 2);
        for (i = 0, length = toLength(O.length), result = allocate(this, length); length > i; i++) {
          result[i] = mapping ? mapfn(O[i], i) : O[i];
        }
        return result;
      };
    
      var $of = function of(/* ...items */) {
        var index = 0;
        var length = arguments.length;
        var result = allocate(this, length);
        while (length > index) result[index] = arguments[index++];
        return result;
      };
    
      // iOS Safari 6.x fails here
      var TO_LOCALE_BUG = !!Uint8Array && fails(function () { arrayToLocaleString.call(new Uint8Array(1)); });
    
      var $toLocaleString = function toLocaleString() {
        return arrayToLocaleString.apply(TO_LOCALE_BUG ? arraySlice.call(validate(this)) : validate(this), arguments);
      };
    
      var proto = {
        copyWithin: function copyWithin(target, start /* , end */) {
          return arrayCopyWithin.call(validate(this), target, start, arguments.length > 2 ? arguments[2] : undefined);
        },
        every: function every(callbackfn /* , thisArg */) {
          return arrayEvery(validate(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
        },
        fill: function fill(value /* , start, end */) { // eslint-disable-line no-unused-vars
          return arrayFill.apply(validate(this), arguments);
        },
        filter: function filter(callbackfn /* , thisArg */) {
          return speciesFromList(this, arrayFilter(validate(this), callbackfn,
            arguments.length > 1 ? arguments[1] : undefined));
        },
        find: function find(predicate /* , thisArg */) {
          return arrayFind(validate(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
        },
        findIndex: function findIndex(predicate /* , thisArg */) {
          return arrayFindIndex(validate(this), predicate, arguments.length > 1 ? arguments[1] : undefined);
        },
        forEach: function forEach(callbackfn /* , thisArg */) {
          arrayForEach(validate(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
        },
        indexOf: function indexOf(searchElement /* , fromIndex */) {
          return arrayIndexOf(validate(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
        },
        includes: function includes(searchElement /* , fromIndex */) {
          return arrayIncludes(validate(this), searchElement, arguments.length > 1 ? arguments[1] : undefined);
        },
        join: function join(separator) { // eslint-disable-line no-unused-vars
          return arrayJoin.apply(validate(this), arguments);
        },
        lastIndexOf: function lastIndexOf(searchElement /* , fromIndex */) { // eslint-disable-line no-unused-vars
          return arrayLastIndexOf.apply(validate(this), arguments);
        },
        map: function map(mapfn /* , thisArg */) {
          return $map(validate(this), mapfn, arguments.length > 1 ? arguments[1] : undefined);
        },
        reduce: function reduce(callbackfn /* , initialValue */) { // eslint-disable-line no-unused-vars
          return arrayReduce.apply(validate(this), arguments);
        },
        reduceRight: function reduceRight(callbackfn /* , initialValue */) { // eslint-disable-line no-unused-vars
          return arrayReduceRight.apply(validate(this), arguments);
        },
        reverse: function reverse() {
          var that = this;
          var length = validate(that).length;
          var middle = Math.floor(length / 2);
          var index = 0;
          var value;
          while (index < middle) {
            value = that[index];
            that[index++] = that[--length];
            that[length] = value;
          } return that;
        },
        some: function some(callbackfn /* , thisArg */) {
          return arraySome(validate(this), callbackfn, arguments.length > 1 ? arguments[1] : undefined);
        },
        sort: function sort(comparefn) {
          return arraySort.call(validate(this), comparefn);
        },
        subarray: function subarray(begin, end) {
          var O = validate(this);
          var length = O.length;
          var $begin = toAbsoluteIndex(begin, length);
          return new (speciesConstructor(O, O[DEF_CONSTRUCTOR]))(
            O.buffer,
            O.byteOffset + $begin * O.BYTES_PER_ELEMENT,
            toLength((end === undefined ? length : toAbsoluteIndex(end, length)) - $begin)
          );
        }
      };
    
      var $slice = function slice(start, end) {
        return speciesFromList(this, arraySlice.call(validate(this), start, end));
      };
    
      var $set = function set(arrayLike /* , offset */) {
        validate(this);
        var offset = toOffset(arguments[1], 1);
        var length = this.length;
        var src = toObject(arrayLike);
        var len = toLength(src.length);
        var index = 0;
        if (len + offset > length) throw RangeError(WRONG_LENGTH);
        while (index < len) this[offset + index] = src[index++];
      };
    
      var $iterators = {
        entries: function entries() {
          return arrayEntries.call(validate(this));
        },
        keys: function keys() {
          return arrayKeys.call(validate(this));
        },
        values: function values() {
          return arrayValues.call(validate(this));
        }
      };
    
      var isTAIndex = function (target, key) {
        return isObject(target)
          && target[TYPED_ARRAY]
          && typeof key != 'symbol'
          && key in target
          && String(+key) == String(key);
      };
      var $getDesc = function getOwnPropertyDescriptor(target, key) {
        return isTAIndex(target, key = toPrimitive(key, true))
          ? propertyDesc(2, target[key])
          : gOPD(target, key);
      };
      var $setDesc = function defineProperty(target, key, desc) {
        if (isTAIndex(target, key = toPrimitive(key, true))
          && isObject(desc)
          && has(desc, 'value')
          && !has(desc, 'get')
          && !has(desc, 'set')
          // TODO: add validation descriptor w/o calling accessors
          && !desc.configurable
          && (!has(desc, 'writable') || desc.writable)
          && (!has(desc, 'enumerable') || desc.enumerable)
        ) {
          target[key] = desc.value;
          return target;
        } return dP(target, key, desc);
      };
    
      if (!ALL_CONSTRUCTORS) {
        $GOPD.f = $getDesc;
        $DP.f = $setDesc;
      }
    
      $export($export.S + $export.F * !ALL_CONSTRUCTORS, 'Object', {
        getOwnPropertyDescriptor: $getDesc,
        defineProperty: $setDesc
      });
    
      if (fails(function () { arrayToString.call({}); })) {
        arrayToString = arrayToLocaleString = function toString() {
          return arrayJoin.call(this);
        };
      }
    
      var $TypedArrayPrototype$ = redefineAll({}, proto);
      redefineAll($TypedArrayPrototype$, $iterators);
      hide($TypedArrayPrototype$, ITERATOR, $iterators.values);
      redefineAll($TypedArrayPrototype$, {
        slice: $slice,
        set: $set,
        constructor: function () { /* noop */ },
        toString: arrayToString,
        toLocaleString: $toLocaleString
      });
      addGetter($TypedArrayPrototype$, 'buffer', 'b');
      addGetter($TypedArrayPrototype$, 'byteOffset', 'o');
      addGetter($TypedArrayPrototype$, 'byteLength', 'l');
      addGetter($TypedArrayPrototype$, 'length', 'e');
      dP($TypedArrayPrototype$, TAG, {
        get: function () { return this[TYPED_ARRAY]; }
      });
    
      // eslint-disable-next-line max-statements
      module.exports = function (KEY, BYTES, wrapper, CLAMPED) {
        CLAMPED = !!CLAMPED;
        var NAME = KEY + (CLAMPED ? 'Clamped' : '') + 'Array';
        var GETTER = 'get' + KEY;
        var SETTER = 'set' + KEY;
        var TypedArray = global[NAME];
        var Base = TypedArray || {};
        var TAC = TypedArray && getPrototypeOf(TypedArray);
        var FORCED = !TypedArray || !$typed.ABV;
        var O = {};
        var TypedArrayPrototype = TypedArray && TypedArray[PROTOTYPE];
        var getter = function (that, index) {
          var data = that._d;
          return data.v[GETTER](index * BYTES + data.o, LITTLE_ENDIAN);
        };
        var setter = function (that, index, value) {
          var data = that._d;
          if (CLAMPED) value = (value = Math.round(value)) < 0 ? 0 : value > 0xff ? 0xff : value & 0xff;
          data.v[SETTER](index * BYTES + data.o, value, LITTLE_ENDIAN);
        };
        var addElement = function (that, index) {
          dP(that, index, {
            get: function () {
              return getter(this, index);
            },
            set: function (value) {
              return setter(this, index, value);
            },
            enumerable: true
          });
        };
        if (FORCED) {
          TypedArray = wrapper(function (that, data, $offset, $length) {
            anInstance(that, TypedArray, NAME, '_d');
            var index = 0;
            var offset = 0;
            var buffer, byteLength, length, klass;
            if (!isObject(data)) {
              length = toIndex(data);
              byteLength = length * BYTES;
              buffer = new $ArrayBuffer(byteLength);
            } else if (data instanceof $ArrayBuffer || (klass = classof(data)) == ARRAY_BUFFER || klass == SHARED_BUFFER) {
              buffer = data;
              offset = toOffset($offset, BYTES);
              var $len = data.byteLength;
              if ($length === undefined) {
                if ($len % BYTES) throw RangeError(WRONG_LENGTH);
                byteLength = $len - offset;
                if (byteLength < 0) throw RangeError(WRONG_LENGTH);
              } else {
                byteLength = toLength($length) * BYTES;
                if (byteLength + offset > $len) throw RangeError(WRONG_LENGTH);
              }
              length = byteLength / BYTES;
            } else if (TYPED_ARRAY in data) {
              return fromList(TypedArray, data);
            } else {
              return $from.call(TypedArray, data);
            }
            hide(that, '_d', {
              b: buffer,
              o: offset,
              l: byteLength,
              e: length,
              v: new $DataView(buffer)
            });
            while (index < length) addElement(that, index++);
          });
          TypedArrayPrototype = TypedArray[PROTOTYPE] = create($TypedArrayPrototype$);
          hide(TypedArrayPrototype, 'constructor', TypedArray);
        } else if (!fails(function () {
          TypedArray(1);
        }) || !fails(function () {
          new TypedArray(-1); // eslint-disable-line no-new
        }) || !$iterDetect(function (iter) {
          new TypedArray(); // eslint-disable-line no-new
          new TypedArray(null); // eslint-disable-line no-new
          new TypedArray(1.5); // eslint-disable-line no-new
          new TypedArray(iter); // eslint-disable-line no-new
        }, true)) {
          TypedArray = wrapper(function (that, data, $offset, $length) {
            anInstance(that, TypedArray, NAME);
            var klass;
            // `ws` module bug, temporarily remove validation length for Uint8Array
            // https://github.com/websockets/ws/pull/645
            if (!isObject(data)) return new Base(toIndex(data));
            if (data instanceof $ArrayBuffer || (klass = classof(data)) == ARRAY_BUFFER || klass == SHARED_BUFFER) {
              return $length !== undefined
                ? new Base(data, toOffset($offset, BYTES), $length)
                : $offset !== undefined
                  ? new Base(data, toOffset($offset, BYTES))
                  : new Base(data);
            }
            if (TYPED_ARRAY in data) return fromList(TypedArray, data);
            return $from.call(TypedArray, data);
          });
          arrayForEach(TAC !== Function.prototype ? gOPN(Base).concat(gOPN(TAC)) : gOPN(Base), function (key) {
            if (!(key in TypedArray)) hide(TypedArray, key, Base[key]);
          });
          TypedArray[PROTOTYPE] = TypedArrayPrototype;
          if (!LIBRARY) TypedArrayPrototype.constructor = TypedArray;
        }
        var $nativeIterator = TypedArrayPrototype[ITERATOR];
        var CORRECT_ITER_NAME = !!$nativeIterator
          && ($nativeIterator.name == 'values' || $nativeIterator.name == undefined);
        var $iterator = $iterators.values;
        hide(TypedArray, TYPED_CONSTRUCTOR, true);
        hide(TypedArrayPrototype, TYPED_ARRAY, NAME);
        hide(TypedArrayPrototype, VIEW, true);
        hide(TypedArrayPrototype, DEF_CONSTRUCTOR, TypedArray);
    
        if (CLAMPED ? new TypedArray(1)[TAG] != NAME : !(TAG in TypedArrayPrototype)) {
          dP(TypedArrayPrototype, TAG, {
            get: function () { return NAME; }
          });
        }
    
        O[NAME] = TypedArray;
    
        $export($export.G + $export.W + $export.F * (TypedArray != Base), O);
    
        $export($export.S, NAME, {
          BYTES_PER_ELEMENT: BYTES
        });
    
        $export($export.S + $export.F * fails(function () { Base.of.call(TypedArray, 1); }), NAME, {
          from: $from,
          of: $of
        });
    
        if (!(BYTES_PER_ELEMENT in TypedArrayPrototype)) hide(TypedArrayPrototype, BYTES_PER_ELEMENT, BYTES);
    
        $export($export.P, NAME, proto);
    
        setSpecies(NAME);
    
        $export($export.P + $export.F * FORCED_SET, NAME, { set: $set });
    
        $export($export.P + $export.F * !CORRECT_ITER_NAME, NAME, $iterators);
    
        if (!LIBRARY && TypedArrayPrototype.toString != arrayToString) TypedArrayPrototype.toString = arrayToString;
    
        $export($export.P + $export.F * fails(function () {
          new TypedArray(1).slice();
        }), NAME, { slice: $slice });
    
        $export($export.P + $export.F * (fails(function () {
          return [1, 2].toLocaleString() != new TypedArray([1, 2]).toLocaleString();
        }) || !fails(function () {
          TypedArrayPrototype.toLocaleString.call([1, 2]);
        })), NAME, { toLocaleString: $toLocaleString });
    
        Iterators[NAME] = CORRECT_ITER_NAME ? $nativeIterator : $iterator;
        if (!LIBRARY && !CORRECT_ITER_NAME) hide(TypedArrayPrototype, ITERATOR, $iterator);
      };
    } else module.exports = function () { /* empty */ };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_typed-buffer.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/_typed-buffer.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var DESCRIPTORS = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js");
    var LIBRARY = __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js");
    var $typed = __webpack_require__(/*! ./_typed */ "./node_modules/core-js/modules/_typed.js");
    var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
    var redefineAll = __webpack_require__(/*! ./_redefine-all */ "./node_modules/core-js/modules/_redefine-all.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var anInstance = __webpack_require__(/*! ./_an-instance */ "./node_modules/core-js/modules/_an-instance.js");
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var toIndex = __webpack_require__(/*! ./_to-index */ "./node_modules/core-js/modules/_to-index.js");
    var gOPN = __webpack_require__(/*! ./_object-gopn */ "./node_modules/core-js/modules/_object-gopn.js").f;
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f;
    var arrayFill = __webpack_require__(/*! ./_array-fill */ "./node_modules/core-js/modules/_array-fill.js");
    var setToStringTag = __webpack_require__(/*! ./_set-to-string-tag */ "./node_modules/core-js/modules/_set-to-string-tag.js");
    var ARRAY_BUFFER = 'ArrayBuffer';
    var DATA_VIEW = 'DataView';
    var PROTOTYPE = 'prototype';
    var WRONG_LENGTH = 'Wrong length!';
    var WRONG_INDEX = 'Wrong index!';
    var $ArrayBuffer = global[ARRAY_BUFFER];
    var $DataView = global[DATA_VIEW];
    var Math = global.Math;
    var RangeError = global.RangeError;
    // eslint-disable-next-line no-shadow-restricted-names
    var Infinity = global.Infinity;
    var BaseBuffer = $ArrayBuffer;
    var abs = Math.abs;
    var pow = Math.pow;
    var floor = Math.floor;
    var log = Math.log;
    var LN2 = Math.LN2;
    var BUFFER = 'buffer';
    var BYTE_LENGTH = 'byteLength';
    var BYTE_OFFSET = 'byteOffset';
    var $BUFFER = DESCRIPTORS ? '_b' : BUFFER;
    var $LENGTH = DESCRIPTORS ? '_l' : BYTE_LENGTH;
    var $OFFSET = DESCRIPTORS ? '_o' : BYTE_OFFSET;
    
    // IEEE754 conversions based on https://github.com/feross/ieee754
    function packIEEE754(value, mLen, nBytes) {
      var buffer = new Array(nBytes);
      var eLen = nBytes * 8 - mLen - 1;
      var eMax = (1 << eLen) - 1;
      var eBias = eMax >> 1;
      var rt = mLen === 23 ? pow(2, -24) - pow(2, -77) : 0;
      var i = 0;
      var s = value < 0 || value === 0 && 1 / value < 0 ? 1 : 0;
      var e, m, c;
      value = abs(value);
      // eslint-disable-next-line no-self-compare
      if (value != value || value === Infinity) {
        // eslint-disable-next-line no-self-compare
        m = value != value ? 1 : 0;
        e = eMax;
      } else {
        e = floor(log(value) / LN2);
        if (value * (c = pow(2, -e)) < 1) {
          e--;
          c *= 2;
        }
        if (e + eBias >= 1) {
          value += rt / c;
        } else {
          value += rt * pow(2, 1 - eBias);
        }
        if (value * c >= 2) {
          e++;
          c /= 2;
        }
        if (e + eBias >= eMax) {
          m = 0;
          e = eMax;
        } else if (e + eBias >= 1) {
          m = (value * c - 1) * pow(2, mLen);
          e = e + eBias;
        } else {
          m = value * pow(2, eBias - 1) * pow(2, mLen);
          e = 0;
        }
      }
      for (; mLen >= 8; buffer[i++] = m & 255, m /= 256, mLen -= 8);
      e = e << mLen | m;
      eLen += mLen;
      for (; eLen > 0; buffer[i++] = e & 255, e /= 256, eLen -= 8);
      buffer[--i] |= s * 128;
      return buffer;
    }
    function unpackIEEE754(buffer, mLen, nBytes) {
      var eLen = nBytes * 8 - mLen - 1;
      var eMax = (1 << eLen) - 1;
      var eBias = eMax >> 1;
      var nBits = eLen - 7;
      var i = nBytes - 1;
      var s = buffer[i--];
      var e = s & 127;
      var m;
      s >>= 7;
      for (; nBits > 0; e = e * 256 + buffer[i], i--, nBits -= 8);
      m = e & (1 << -nBits) - 1;
      e >>= -nBits;
      nBits += mLen;
      for (; nBits > 0; m = m * 256 + buffer[i], i--, nBits -= 8);
      if (e === 0) {
        e = 1 - eBias;
      } else if (e === eMax) {
        return m ? NaN : s ? -Infinity : Infinity;
      } else {
        m = m + pow(2, mLen);
        e = e - eBias;
      } return (s ? -1 : 1) * m * pow(2, e - mLen);
    }
    
    function unpackI32(bytes) {
      return bytes[3] << 24 | bytes[2] << 16 | bytes[1] << 8 | bytes[0];
    }
    function packI8(it) {
      return [it & 0xff];
    }
    function packI16(it) {
      return [it & 0xff, it >> 8 & 0xff];
    }
    function packI32(it) {
      return [it & 0xff, it >> 8 & 0xff, it >> 16 & 0xff, it >> 24 & 0xff];
    }
    function packF64(it) {
      return packIEEE754(it, 52, 8);
    }
    function packF32(it) {
      return packIEEE754(it, 23, 4);
    }
    
    function addGetter(C, key, internal) {
      dP(C[PROTOTYPE], key, { get: function () { return this[internal]; } });
    }
    
    function get(view, bytes, index, isLittleEndian) {
      var numIndex = +index;
      var intIndex = toIndex(numIndex);
      if (intIndex + bytes > view[$LENGTH]) throw RangeError(WRONG_INDEX);
      var store = view[$BUFFER]._b;
      var start = intIndex + view[$OFFSET];
      var pack = store.slice(start, start + bytes);
      return isLittleEndian ? pack : pack.reverse();
    }
    function set(view, bytes, index, conversion, value, isLittleEndian) {
      var numIndex = +index;
      var intIndex = toIndex(numIndex);
      if (intIndex + bytes > view[$LENGTH]) throw RangeError(WRONG_INDEX);
      var store = view[$BUFFER]._b;
      var start = intIndex + view[$OFFSET];
      var pack = conversion(+value);
      for (var i = 0; i < bytes; i++) store[start + i] = pack[isLittleEndian ? i : bytes - i - 1];
    }
    
    if (!$typed.ABV) {
      $ArrayBuffer = function ArrayBuffer(length) {
        anInstance(this, $ArrayBuffer, ARRAY_BUFFER);
        var byteLength = toIndex(length);
        this._b = arrayFill.call(new Array(byteLength), 0);
        this[$LENGTH] = byteLength;
      };
    
      $DataView = function DataView(buffer, byteOffset, byteLength) {
        anInstance(this, $DataView, DATA_VIEW);
        anInstance(buffer, $ArrayBuffer, DATA_VIEW);
        var bufferLength = buffer[$LENGTH];
        var offset = toInteger(byteOffset);
        if (offset < 0 || offset > bufferLength) throw RangeError('Wrong offset!');
        byteLength = byteLength === undefined ? bufferLength - offset : toLength(byteLength);
        if (offset + byteLength > bufferLength) throw RangeError(WRONG_LENGTH);
        this[$BUFFER] = buffer;
        this[$OFFSET] = offset;
        this[$LENGTH] = byteLength;
      };
    
      if (DESCRIPTORS) {
        addGetter($ArrayBuffer, BYTE_LENGTH, '_l');
        addGetter($DataView, BUFFER, '_b');
        addGetter($DataView, BYTE_LENGTH, '_l');
        addGetter($DataView, BYTE_OFFSET, '_o');
      }
    
      redefineAll($DataView[PROTOTYPE], {
        getInt8: function getInt8(byteOffset) {
          return get(this, 1, byteOffset)[0] << 24 >> 24;
        },
        getUint8: function getUint8(byteOffset) {
          return get(this, 1, byteOffset)[0];
        },
        getInt16: function getInt16(byteOffset /* , littleEndian */) {
          var bytes = get(this, 2, byteOffset, arguments[1]);
          return (bytes[1] << 8 | bytes[0]) << 16 >> 16;
        },
        getUint16: function getUint16(byteOffset /* , littleEndian */) {
          var bytes = get(this, 2, byteOffset, arguments[1]);
          return bytes[1] << 8 | bytes[0];
        },
        getInt32: function getInt32(byteOffset /* , littleEndian */) {
          return unpackI32(get(this, 4, byteOffset, arguments[1]));
        },
        getUint32: function getUint32(byteOffset /* , littleEndian */) {
          return unpackI32(get(this, 4, byteOffset, arguments[1])) >>> 0;
        },
        getFloat32: function getFloat32(byteOffset /* , littleEndian */) {
          return unpackIEEE754(get(this, 4, byteOffset, arguments[1]), 23, 4);
        },
        getFloat64: function getFloat64(byteOffset /* , littleEndian */) {
          return unpackIEEE754(get(this, 8, byteOffset, arguments[1]), 52, 8);
        },
        setInt8: function setInt8(byteOffset, value) {
          set(this, 1, byteOffset, packI8, value);
        },
        setUint8: function setUint8(byteOffset, value) {
          set(this, 1, byteOffset, packI8, value);
        },
        setInt16: function setInt16(byteOffset, value /* , littleEndian */) {
          set(this, 2, byteOffset, packI16, value, arguments[2]);
        },
        setUint16: function setUint16(byteOffset, value /* , littleEndian */) {
          set(this, 2, byteOffset, packI16, value, arguments[2]);
        },
        setInt32: function setInt32(byteOffset, value /* , littleEndian */) {
          set(this, 4, byteOffset, packI32, value, arguments[2]);
        },
        setUint32: function setUint32(byteOffset, value /* , littleEndian */) {
          set(this, 4, byteOffset, packI32, value, arguments[2]);
        },
        setFloat32: function setFloat32(byteOffset, value /* , littleEndian */) {
          set(this, 4, byteOffset, packF32, value, arguments[2]);
        },
        setFloat64: function setFloat64(byteOffset, value /* , littleEndian */) {
          set(this, 8, byteOffset, packF64, value, arguments[2]);
        }
      });
    } else {
      if (!fails(function () {
        $ArrayBuffer(1);
      }) || !fails(function () {
        new $ArrayBuffer(-1); // eslint-disable-line no-new
      }) || fails(function () {
        new $ArrayBuffer(); // eslint-disable-line no-new
        new $ArrayBuffer(1.5); // eslint-disable-line no-new
        new $ArrayBuffer(NaN); // eslint-disable-line no-new
        return $ArrayBuffer.name != ARRAY_BUFFER;
      })) {
        $ArrayBuffer = function ArrayBuffer(length) {
          anInstance(this, $ArrayBuffer);
          return new BaseBuffer(toIndex(length));
        };
        var ArrayBufferProto = $ArrayBuffer[PROTOTYPE] = BaseBuffer[PROTOTYPE];
        for (var keys = gOPN(BaseBuffer), j = 0, key; keys.length > j;) {
          if (!((key = keys[j++]) in $ArrayBuffer)) hide($ArrayBuffer, key, BaseBuffer[key]);
        }
        if (!LIBRARY) ArrayBufferProto.constructor = $ArrayBuffer;
      }
      // iOS Safari 7.x bug
      var view = new $DataView(new $ArrayBuffer(2));
      var $setInt8 = $DataView[PROTOTYPE].setInt8;
      view.setInt8(0, 2147483648);
      view.setInt8(1, 2147483649);
      if (view.getInt8(0) || !view.getInt8(1)) redefineAll($DataView[PROTOTYPE], {
        setInt8: function setInt8(byteOffset, value) {
          $setInt8.call(this, byteOffset, value << 24 >> 24);
        },
        setUint8: function setUint8(byteOffset, value) {
          $setInt8.call(this, byteOffset, value << 24 >> 24);
        }
      }, true);
    }
    setToStringTag($ArrayBuffer, ARRAY_BUFFER);
    setToStringTag($DataView, DATA_VIEW);
    hide($DataView[PROTOTYPE], $typed.VIEW, true);
    exports[ARRAY_BUFFER] = $ArrayBuffer;
    exports[DATA_VIEW] = $DataView;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_typed.js":
    /*!************************************************!*\
      !*** ./node_modules/core-js/modules/_typed.js ***!
      \************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
    var uid = __webpack_require__(/*! ./_uid */ "./node_modules/core-js/modules/_uid.js");
    var TYPED = uid('typed_array');
    var VIEW = uid('view');
    var ABV = !!(global.ArrayBuffer && global.DataView);
    var CONSTR = ABV;
    var i = 0;
    var l = 9;
    var Typed;
    
    var TypedArrayConstructors = (
      'Int8Array,Uint8Array,Uint8ClampedArray,Int16Array,Uint16Array,Int32Array,Uint32Array,Float32Array,Float64Array'
    ).split(',');
    
    while (i < l) {
      if (Typed = global[TypedArrayConstructors[i++]]) {
        hide(Typed.prototype, TYPED, true);
        hide(Typed.prototype, VIEW, true);
      } else CONSTR = false;
    }
    
    module.exports = {
      ABV: ABV,
      CONSTR: CONSTR,
      TYPED: TYPED,
      VIEW: VIEW
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_uid.js":
    /*!**********************************************!*\
      !*** ./node_modules/core-js/modules/_uid.js ***!
      \**********************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    var id = 0;
    var px = Math.random();
    module.exports = function (key) {
      return 'Symbol('.concat(key === undefined ? '' : key, ')_', (++id + px).toString(36));
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_user-agent.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_user-agent.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var navigator = global.navigator;
    
    module.exports = navigator && navigator.userAgent || '';
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_validate-collection.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/_validate-collection.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    module.exports = function (it, TYPE) {
      if (!isObject(it) || it._t !== TYPE) throw TypeError('Incompatible receiver, ' + TYPE + ' required!');
      return it;
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_wks-define.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/_wks-define.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var core = __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js");
    var LIBRARY = __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js");
    var wksExt = __webpack_require__(/*! ./_wks-ext */ "./node_modules/core-js/modules/_wks-ext.js");
    var defineProperty = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f;
    module.exports = function (name) {
      var $Symbol = core.Symbol || (core.Symbol = LIBRARY ? {} : global.Symbol || {});
      if (name.charAt(0) != '_' && !(name in $Symbol)) defineProperty($Symbol, name, { value: wksExt.f(name) });
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_wks-ext.js":
    /*!**************************************************!*\
      !*** ./node_modules/core-js/modules/_wks-ext.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    exports.f = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js");
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/_wks.js":
    /*!**********************************************!*\
      !*** ./node_modules/core-js/modules/_wks.js ***!
      \**********************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var store = __webpack_require__(/*! ./_shared */ "./node_modules/core-js/modules/_shared.js")('wks');
    var uid = __webpack_require__(/*! ./_uid */ "./node_modules/core-js/modules/_uid.js");
    var Symbol = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").Symbol;
    var USE_SYMBOL = typeof Symbol == 'function';
    
    var $exports = module.exports = function (name) {
      return store[name] || (store[name] =
        USE_SYMBOL && Symbol[name] || (USE_SYMBOL ? Symbol : uid)('Symbol.' + name));
    };
    
    $exports.store = store;
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/core.get-iterator-method.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/core.get-iterator-method.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var classof = __webpack_require__(/*! ./_classof */ "./node_modules/core-js/modules/_classof.js");
    var ITERATOR = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('iterator');
    var Iterators = __webpack_require__(/*! ./_iterators */ "./node_modules/core-js/modules/_iterators.js");
    module.exports = __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js").getIteratorMethod = function (it) {
      if (it != undefined) return it[ITERATOR]
        || it['@@iterator']
        || Iterators[classof(it)];
    };
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/core.regexp.escape.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/core.regexp.escape.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/benjamingr/RexExp.escape
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $re = __webpack_require__(/*! ./_replacer */ "./node_modules/core-js/modules/_replacer.js")(/[\\^$*+?.()|[\]{}]/g, '\\$&');
    
    $export($export.S, 'RegExp', { escape: function escape(it) { return $re(it); } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.copy-within.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.copy-within.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 22.1.3.3 Array.prototype.copyWithin(target, start, end = this.length)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.P, 'Array', { copyWithin: __webpack_require__(/*! ./_array-copy-within */ "./node_modules/core-js/modules/_array-copy-within.js") });
    
    __webpack_require__(/*! ./_add-to-unscopables */ "./node_modules/core-js/modules/_add-to-unscopables.js")('copyWithin');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.every.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.every.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $every = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js")(4);
    
    $export($export.P + $export.F * !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")([].every, true), 'Array', {
      // 22.1.3.5 / 15.4.4.16 Array.prototype.every(callbackfn [, thisArg])
      every: function every(callbackfn /* , thisArg */) {
        return $every(this, callbackfn, arguments[1]);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.fill.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.fill.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 22.1.3.6 Array.prototype.fill(value, start = 0, end = this.length)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.P, 'Array', { fill: __webpack_require__(/*! ./_array-fill */ "./node_modules/core-js/modules/_array-fill.js") });
    
    __webpack_require__(/*! ./_add-to-unscopables */ "./node_modules/core-js/modules/_add-to-unscopables.js")('fill');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.filter.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.filter.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $filter = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js")(2);
    
    $export($export.P + $export.F * !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")([].filter, true), 'Array', {
      // 22.1.3.7 / 15.4.4.20 Array.prototype.filter(callbackfn [, thisArg])
      filter: function filter(callbackfn /* , thisArg */) {
        return $filter(this, callbackfn, arguments[1]);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.find-index.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.find-index.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 22.1.3.9 Array.prototype.findIndex(predicate, thisArg = undefined)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $find = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js")(6);
    var KEY = 'findIndex';
    var forced = true;
    // Shouldn't skip holes
    if (KEY in []) Array(1)[KEY](function () { forced = false; });
    $export($export.P + $export.F * forced, 'Array', {
      findIndex: function findIndex(callbackfn /* , that = undefined */) {
        return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
      }
    });
    __webpack_require__(/*! ./_add-to-unscopables */ "./node_modules/core-js/modules/_add-to-unscopables.js")(KEY);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.find.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.find.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 22.1.3.8 Array.prototype.find(predicate, thisArg = undefined)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $find = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js")(5);
    var KEY = 'find';
    var forced = true;
    // Shouldn't skip holes
    if (KEY in []) Array(1)[KEY](function () { forced = false; });
    $export($export.P + $export.F * forced, 'Array', {
      find: function find(callbackfn /* , that = undefined */) {
        return $find(this, callbackfn, arguments.length > 1 ? arguments[1] : undefined);
      }
    });
    __webpack_require__(/*! ./_add-to-unscopables */ "./node_modules/core-js/modules/_add-to-unscopables.js")(KEY);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.for-each.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.for-each.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $forEach = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js")(0);
    var STRICT = __webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")([].forEach, true);
    
    $export($export.P + $export.F * !STRICT, 'Array', {
      // 22.1.3.10 / 15.4.4.18 Array.prototype.forEach(callbackfn [, thisArg])
      forEach: function forEach(callbackfn /* , thisArg */) {
        return $forEach(this, callbackfn, arguments[1]);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.from.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.from.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var call = __webpack_require__(/*! ./_iter-call */ "./node_modules/core-js/modules/_iter-call.js");
    var isArrayIter = __webpack_require__(/*! ./_is-array-iter */ "./node_modules/core-js/modules/_is-array-iter.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var createProperty = __webpack_require__(/*! ./_create-property */ "./node_modules/core-js/modules/_create-property.js");
    var getIterFn = __webpack_require__(/*! ./core.get-iterator-method */ "./node_modules/core-js/modules/core.get-iterator-method.js");
    
    $export($export.S + $export.F * !__webpack_require__(/*! ./_iter-detect */ "./node_modules/core-js/modules/_iter-detect.js")(function (iter) { Array.from(iter); }), 'Array', {
      // 22.1.2.1 Array.from(arrayLike, mapfn = undefined, thisArg = undefined)
      from: function from(arrayLike /* , mapfn = undefined, thisArg = undefined */) {
        var O = toObject(arrayLike);
        var C = typeof this == 'function' ? this : Array;
        var aLen = arguments.length;
        var mapfn = aLen > 1 ? arguments[1] : undefined;
        var mapping = mapfn !== undefined;
        var index = 0;
        var iterFn = getIterFn(O);
        var length, result, step, iterator;
        if (mapping) mapfn = ctx(mapfn, aLen > 2 ? arguments[2] : undefined, 2);
        // if object isn't iterable or it's array with default iterator - use simple case
        if (iterFn != undefined && !(C == Array && isArrayIter(iterFn))) {
          for (iterator = iterFn.call(O), result = new C(); !(step = iterator.next()).done; index++) {
            createProperty(result, index, mapping ? call(iterator, mapfn, [step.value, index], true) : step.value);
          }
        } else {
          length = toLength(O.length);
          for (result = new C(length); length > index; index++) {
            createProperty(result, index, mapping ? mapfn(O[index], index) : O[index]);
          }
        }
        result.length = index;
        return result;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.index-of.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.index-of.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $indexOf = __webpack_require__(/*! ./_array-includes */ "./node_modules/core-js/modules/_array-includes.js")(false);
    var $native = [].indexOf;
    var NEGATIVE_ZERO = !!$native && 1 / [1].indexOf(1, -0) < 0;
    
    $export($export.P + $export.F * (NEGATIVE_ZERO || !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")($native)), 'Array', {
      // 22.1.3.11 / 15.4.4.14 Array.prototype.indexOf(searchElement [, fromIndex])
      indexOf: function indexOf(searchElement /* , fromIndex = 0 */) {
        return NEGATIVE_ZERO
          // convert -0 to +0
          ? $native.apply(this, arguments) || 0
          : $indexOf(this, searchElement, arguments[1]);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.is-array.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.is-array.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 22.1.2.2 / 15.4.3.2 Array.isArray(arg)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Array', { isArray: __webpack_require__(/*! ./_is-array */ "./node_modules/core-js/modules/_is-array.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.iterator.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.iterator.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var addToUnscopables = __webpack_require__(/*! ./_add-to-unscopables */ "./node_modules/core-js/modules/_add-to-unscopables.js");
    var step = __webpack_require__(/*! ./_iter-step */ "./node_modules/core-js/modules/_iter-step.js");
    var Iterators = __webpack_require__(/*! ./_iterators */ "./node_modules/core-js/modules/_iterators.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    
    // 22.1.3.4 Array.prototype.entries()
    // 22.1.3.13 Array.prototype.keys()
    // 22.1.3.29 Array.prototype.values()
    // 22.1.3.30 Array.prototype[@@iterator]()
    module.exports = __webpack_require__(/*! ./_iter-define */ "./node_modules/core-js/modules/_iter-define.js")(Array, 'Array', function (iterated, kind) {
      this._t = toIObject(iterated); // target
      this._i = 0;                   // next index
      this._k = kind;                // kind
    // 22.1.5.2.1 %ArrayIteratorPrototype%.next()
    }, function () {
      var O = this._t;
      var kind = this._k;
      var index = this._i++;
      if (!O || index >= O.length) {
        this._t = undefined;
        return step(1);
      }
      if (kind == 'keys') return step(0, index);
      if (kind == 'values') return step(0, O[index]);
      return step(0, [index, O[index]]);
    }, 'values');
    
    // argumentsList[@@iterator] is %ArrayProto_values% (9.4.4.6, 9.4.4.7)
    Iterators.Arguments = Iterators.Array;
    
    addToUnscopables('keys');
    addToUnscopables('values');
    addToUnscopables('entries');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.join.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.join.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 22.1.3.13 Array.prototype.join(separator)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var arrayJoin = [].join;
    
    // fallback for not array-like strings
    $export($export.P + $export.F * (__webpack_require__(/*! ./_iobject */ "./node_modules/core-js/modules/_iobject.js") != Object || !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")(arrayJoin)), 'Array', {
      join: function join(separator) {
        return arrayJoin.call(toIObject(this), separator === undefined ? ',' : separator);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.last-index-of.js":
    /*!*****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.last-index-of.js ***!
      \*****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var $native = [].lastIndexOf;
    var NEGATIVE_ZERO = !!$native && 1 / [1].lastIndexOf(1, -0) < 0;
    
    $export($export.P + $export.F * (NEGATIVE_ZERO || !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")($native)), 'Array', {
      // 22.1.3.14 / 15.4.4.15 Array.prototype.lastIndexOf(searchElement [, fromIndex])
      lastIndexOf: function lastIndexOf(searchElement /* , fromIndex = @[*-1] */) {
        // convert -0 to +0
        if (NEGATIVE_ZERO) return $native.apply(this, arguments) || 0;
        var O = toIObject(this);
        var length = toLength(O.length);
        var index = length - 1;
        if (arguments.length > 1) index = Math.min(index, toInteger(arguments[1]));
        if (index < 0) index = length + index;
        for (;index >= 0; index--) if (index in O) if (O[index] === searchElement) return index || 0;
        return -1;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.map.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.map.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $map = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js")(1);
    
    $export($export.P + $export.F * !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")([].map, true), 'Array', {
      // 22.1.3.15 / 15.4.4.19 Array.prototype.map(callbackfn [, thisArg])
      map: function map(callbackfn /* , thisArg */) {
        return $map(this, callbackfn, arguments[1]);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.of.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.of.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var createProperty = __webpack_require__(/*! ./_create-property */ "./node_modules/core-js/modules/_create-property.js");
    
    // WebKit Array.of isn't generic
    $export($export.S + $export.F * __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      function F() { /* empty */ }
      return !(Array.of.call(F) instanceof F);
    }), 'Array', {
      // 22.1.2.3 Array.of( ...items)
      of: function of(/* ...args */) {
        var index = 0;
        var aLen = arguments.length;
        var result = new (typeof this == 'function' ? this : Array)(aLen);
        while (aLen > index) createProperty(result, index, arguments[index++]);
        result.length = aLen;
        return result;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.reduce-right.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.reduce-right.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $reduce = __webpack_require__(/*! ./_array-reduce */ "./node_modules/core-js/modules/_array-reduce.js");
    
    $export($export.P + $export.F * !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")([].reduceRight, true), 'Array', {
      // 22.1.3.19 / 15.4.4.22 Array.prototype.reduceRight(callbackfn [, initialValue])
      reduceRight: function reduceRight(callbackfn /* , initialValue */) {
        return $reduce(this, callbackfn, arguments.length, arguments[1], true);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.reduce.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.reduce.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $reduce = __webpack_require__(/*! ./_array-reduce */ "./node_modules/core-js/modules/_array-reduce.js");
    
    $export($export.P + $export.F * !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")([].reduce, true), 'Array', {
      // 22.1.3.18 / 15.4.4.21 Array.prototype.reduce(callbackfn [, initialValue])
      reduce: function reduce(callbackfn /* , initialValue */) {
        return $reduce(this, callbackfn, arguments.length, arguments[1], false);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.slice.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.slice.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var html = __webpack_require__(/*! ./_html */ "./node_modules/core-js/modules/_html.js");
    var cof = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js");
    var toAbsoluteIndex = __webpack_require__(/*! ./_to-absolute-index */ "./node_modules/core-js/modules/_to-absolute-index.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var arraySlice = [].slice;
    
    // fallback for not array-like ES3 strings and DOM objects
    $export($export.P + $export.F * __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      if (html) arraySlice.call(html);
    }), 'Array', {
      slice: function slice(begin, end) {
        var len = toLength(this.length);
        var klass = cof(this);
        end = end === undefined ? len : end;
        if (klass == 'Array') return arraySlice.call(this, begin, end);
        var start = toAbsoluteIndex(begin, len);
        var upTo = toAbsoluteIndex(end, len);
        var size = toLength(upTo - start);
        var cloned = new Array(size);
        var i = 0;
        for (; i < size; i++) cloned[i] = klass == 'String'
          ? this.charAt(start + i)
          : this[start + i];
        return cloned;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.some.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.some.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $some = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js")(3);
    
    $export($export.P + $export.F * !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")([].some, true), 'Array', {
      // 22.1.3.23 / 15.4.4.17 Array.prototype.some(callbackfn [, thisArg])
      some: function some(callbackfn /* , thisArg */) {
        return $some(this, callbackfn, arguments[1]);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.sort.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.sort.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var $sort = [].sort;
    var test = [1, 2, 3];
    
    $export($export.P + $export.F * (fails(function () {
      // IE8-
      test.sort(undefined);
    }) || !fails(function () {
      // V8 bug
      test.sort(null);
      // Old WebKit
    }) || !__webpack_require__(/*! ./_strict-method */ "./node_modules/core-js/modules/_strict-method.js")($sort)), 'Array', {
      // 22.1.3.25 Array.prototype.sort(comparefn)
      sort: function sort(comparefn) {
        return comparefn === undefined
          ? $sort.call(toObject(this))
          : $sort.call(toObject(this), aFunction(comparefn));
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.array.species.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.array.species.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_set-species */ "./node_modules/core-js/modules/_set-species.js")('Array');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.date.now.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.date.now.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.3.3.1 / 15.9.4.4 Date.now()
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Date', { now: function () { return new Date().getTime(); } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.date.to-iso-string.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.date.to-iso-string.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.3.4.36 / 15.9.5.43 Date.prototype.toISOString()
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toISOString = __webpack_require__(/*! ./_date-to-iso-string */ "./node_modules/core-js/modules/_date-to-iso-string.js");
    
    // PhantomJS / old WebKit has a broken implementations
    $export($export.P + $export.F * (Date.prototype.toISOString !== toISOString), 'Date', {
      toISOString: toISOString
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.date.to-json.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.date.to-json.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    
    $export($export.P + $export.F * __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      return new Date(NaN).toJSON() !== null
        || Date.prototype.toJSON.call({ toISOString: function () { return 1; } }) !== 1;
    }), 'Date', {
      // eslint-disable-next-line no-unused-vars
      toJSON: function toJSON(key) {
        var O = toObject(this);
        var pv = toPrimitive(O);
        return typeof pv == 'number' && !isFinite(pv) ? null : O.toISOString();
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.date.to-primitive.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.date.to-primitive.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var TO_PRIMITIVE = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('toPrimitive');
    var proto = Date.prototype;
    
    if (!(TO_PRIMITIVE in proto)) __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js")(proto, TO_PRIMITIVE, __webpack_require__(/*! ./_date-to-primitive */ "./node_modules/core-js/modules/_date-to-primitive.js"));
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.date.to-string.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.date.to-string.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var DateProto = Date.prototype;
    var INVALID_DATE = 'Invalid Date';
    var TO_STRING = 'toString';
    var $toString = DateProto[TO_STRING];
    var getTime = DateProto.getTime;
    if (new Date(NaN) + '' != INVALID_DATE) {
      __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js")(DateProto, TO_STRING, function toString() {
        var value = getTime.call(this);
        // eslint-disable-next-line no-self-compare
        return value === value ? $toString.call(this) : INVALID_DATE;
      });
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.function.bind.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.function.bind.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.2.3.2 / 15.3.4.5 Function.prototype.bind(thisArg, args...)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.P, 'Function', { bind: __webpack_require__(/*! ./_bind */ "./node_modules/core-js/modules/_bind.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.function.has-instance.js":
    /*!*******************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.function.has-instance.js ***!
      \*******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var HAS_INSTANCE = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('hasInstance');
    var FunctionProto = Function.prototype;
    // 19.2.3.6 Function.prototype[@@hasInstance](V)
    if (!(HAS_INSTANCE in FunctionProto)) __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f(FunctionProto, HAS_INSTANCE, { value: function (O) {
      if (typeof this != 'function' || !isObject(O)) return false;
      if (!isObject(this.prototype)) return O instanceof this;
      // for environment w/o native `@@hasInstance` logic enough `instanceof`, but add this:
      while (O = getPrototypeOf(O)) if (this.prototype === O) return true;
      return false;
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.function.name.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.function.name.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f;
    var FProto = Function.prototype;
    var nameRE = /^\s*function ([^ (]*)/;
    var NAME = 'name';
    
    // 19.2.4.2 name
    NAME in FProto || __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") && dP(FProto, NAME, {
      configurable: true,
      get: function () {
        try {
          return ('' + this).match(nameRE)[1];
        } catch (e) {
          return '';
        }
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.map.js":
    /*!*************************************************!*\
      !*** ./node_modules/core-js/modules/es6.map.js ***!
      \*************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var strong = __webpack_require__(/*! ./_collection-strong */ "./node_modules/core-js/modules/_collection-strong.js");
    var validate = __webpack_require__(/*! ./_validate-collection */ "./node_modules/core-js/modules/_validate-collection.js");
    var MAP = 'Map';
    
    // 23.1 Map Objects
    module.exports = __webpack_require__(/*! ./_collection */ "./node_modules/core-js/modules/_collection.js")(MAP, function (get) {
      return function Map() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
    }, {
      // 23.1.3.6 Map.prototype.get(key)
      get: function get(key) {
        var entry = strong.getEntry(validate(this, MAP), key);
        return entry && entry.v;
      },
      // 23.1.3.9 Map.prototype.set(key, value)
      set: function set(key, value) {
        return strong.def(validate(this, MAP), key === 0 ? 0 : key, value);
      }
    }, strong, true);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.acosh.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.acosh.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.3 Math.acosh(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var log1p = __webpack_require__(/*! ./_math-log1p */ "./node_modules/core-js/modules/_math-log1p.js");
    var sqrt = Math.sqrt;
    var $acosh = Math.acosh;
    
    $export($export.S + $export.F * !($acosh
      // V8 bug: https://code.google.com/p/v8/issues/detail?id=3509
      && Math.floor($acosh(Number.MAX_VALUE)) == 710
      // Tor Browser bug: Math.acosh(Infinity) -> NaN
      && $acosh(Infinity) == Infinity
    ), 'Math', {
      acosh: function acosh(x) {
        return (x = +x) < 1 ? NaN : x > 94906265.62425156
          ? Math.log(x) + Math.LN2
          : log1p(x - 1 + sqrt(x - 1) * sqrt(x + 1));
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.asinh.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.asinh.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.5 Math.asinh(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $asinh = Math.asinh;
    
    function asinh(x) {
      return !isFinite(x = +x) || x == 0 ? x : x < 0 ? -asinh(-x) : Math.log(x + Math.sqrt(x * x + 1));
    }
    
    // Tor Browser bug: Math.asinh(0) -> -0
    $export($export.S + $export.F * !($asinh && 1 / $asinh(0) > 0), 'Math', { asinh: asinh });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.atanh.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.atanh.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.7 Math.atanh(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $atanh = Math.atanh;
    
    // Tor Browser bug: Math.atanh(-0) -> 0
    $export($export.S + $export.F * !($atanh && 1 / $atanh(-0) < 0), 'Math', {
      atanh: function atanh(x) {
        return (x = +x) == 0 ? x : Math.log((1 + x) / (1 - x)) / 2;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.cbrt.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.cbrt.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.9 Math.cbrt(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var sign = __webpack_require__(/*! ./_math-sign */ "./node_modules/core-js/modules/_math-sign.js");
    
    $export($export.S, 'Math', {
      cbrt: function cbrt(x) {
        return sign(x = +x) * Math.pow(Math.abs(x), 1 / 3);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.clz32.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.clz32.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.11 Math.clz32(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      clz32: function clz32(x) {
        return (x >>>= 0) ? 31 - Math.floor(Math.log(x + 0.5) * Math.LOG2E) : 32;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.cosh.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.cosh.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.12 Math.cosh(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var exp = Math.exp;
    
    $export($export.S, 'Math', {
      cosh: function cosh(x) {
        return (exp(x = +x) + exp(-x)) / 2;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.expm1.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.expm1.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.14 Math.expm1(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $expm1 = __webpack_require__(/*! ./_math-expm1 */ "./node_modules/core-js/modules/_math-expm1.js");
    
    $export($export.S + $export.F * ($expm1 != Math.expm1), 'Math', { expm1: $expm1 });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.fround.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.fround.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.16 Math.fround(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', { fround: __webpack_require__(/*! ./_math-fround */ "./node_modules/core-js/modules/_math-fround.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.hypot.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.hypot.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.17 Math.hypot([value1[, value2[, … ]]])
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var abs = Math.abs;
    
    $export($export.S, 'Math', {
      hypot: function hypot(value1, value2) { // eslint-disable-line no-unused-vars
        var sum = 0;
        var i = 0;
        var aLen = arguments.length;
        var larg = 0;
        var arg, div;
        while (i < aLen) {
          arg = abs(arguments[i++]);
          if (larg < arg) {
            div = larg / arg;
            sum = sum * div * div + 1;
            larg = arg;
          } else if (arg > 0) {
            div = arg / larg;
            sum += div * div;
          } else sum += arg;
        }
        return larg === Infinity ? Infinity : larg * Math.sqrt(sum);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.imul.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.imul.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.18 Math.imul(x, y)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $imul = Math.imul;
    
    // some WebKit versions fails with big numbers, some has wrong arity
    $export($export.S + $export.F * __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      return $imul(0xffffffff, 5) != -5 || $imul.length != 2;
    }), 'Math', {
      imul: function imul(x, y) {
        var UINT16 = 0xffff;
        var xn = +x;
        var yn = +y;
        var xl = UINT16 & xn;
        var yl = UINT16 & yn;
        return 0 | xl * yl + ((UINT16 & xn >>> 16) * yl + xl * (UINT16 & yn >>> 16) << 16 >>> 0);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.log10.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.log10.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.21 Math.log10(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      log10: function log10(x) {
        return Math.log(x) * Math.LOG10E;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.log1p.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.log1p.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.20 Math.log1p(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', { log1p: __webpack_require__(/*! ./_math-log1p */ "./node_modules/core-js/modules/_math-log1p.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.log2.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.log2.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.22 Math.log2(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      log2: function log2(x) {
        return Math.log(x) / Math.LN2;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.sign.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.sign.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.28 Math.sign(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', { sign: __webpack_require__(/*! ./_math-sign */ "./node_modules/core-js/modules/_math-sign.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.sinh.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.sinh.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.30 Math.sinh(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var expm1 = __webpack_require__(/*! ./_math-expm1 */ "./node_modules/core-js/modules/_math-expm1.js");
    var exp = Math.exp;
    
    // V8 near Chromium 38 has a problem with very small numbers
    $export($export.S + $export.F * __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      return !Math.sinh(-2e-17) != -2e-17;
    }), 'Math', {
      sinh: function sinh(x) {
        return Math.abs(x = +x) < 1
          ? (expm1(x) - expm1(-x)) / 2
          : (exp(x - 1) - exp(-x - 1)) * (Math.E / 2);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.tanh.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.tanh.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.33 Math.tanh(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var expm1 = __webpack_require__(/*! ./_math-expm1 */ "./node_modules/core-js/modules/_math-expm1.js");
    var exp = Math.exp;
    
    $export($export.S, 'Math', {
      tanh: function tanh(x) {
        var a = expm1(x = +x);
        var b = expm1(-x);
        return a == Infinity ? 1 : b == Infinity ? -1 : (a - b) / (exp(x) + exp(-x));
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.math.trunc.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.math.trunc.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.2.2.34 Math.trunc(x)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      trunc: function trunc(it) {
        return (it > 0 ? Math.floor : Math.ceil)(it);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.constructor.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.constructor.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var cof = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js");
    var inheritIfRequired = __webpack_require__(/*! ./_inherit-if-required */ "./node_modules/core-js/modules/_inherit-if-required.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var gOPN = __webpack_require__(/*! ./_object-gopn */ "./node_modules/core-js/modules/_object-gopn.js").f;
    var gOPD = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js").f;
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f;
    var $trim = __webpack_require__(/*! ./_string-trim */ "./node_modules/core-js/modules/_string-trim.js").trim;
    var NUMBER = 'Number';
    var $Number = global[NUMBER];
    var Base = $Number;
    var proto = $Number.prototype;
    // Opera ~12 has broken Object#toString
    var BROKEN_COF = cof(__webpack_require__(/*! ./_object-create */ "./node_modules/core-js/modules/_object-create.js")(proto)) == NUMBER;
    var TRIM = 'trim' in String.prototype;
    
    // 7.1.3 ToNumber(argument)
    var toNumber = function (argument) {
      var it = toPrimitive(argument, false);
      if (typeof it == 'string' && it.length > 2) {
        it = TRIM ? it.trim() : $trim(it, 3);
        var first = it.charCodeAt(0);
        var third, radix, maxCode;
        if (first === 43 || first === 45) {
          third = it.charCodeAt(2);
          if (third === 88 || third === 120) return NaN; // Number('+0x1') should be NaN, old V8 fix
        } else if (first === 48) {
          switch (it.charCodeAt(1)) {
            case 66: case 98: radix = 2; maxCode = 49; break; // fast equal /^0b[01]+$/i
            case 79: case 111: radix = 8; maxCode = 55; break; // fast equal /^0o[0-7]+$/i
            default: return +it;
          }
          for (var digits = it.slice(2), i = 0, l = digits.length, code; i < l; i++) {
            code = digits.charCodeAt(i);
            // parseInt parses a string to a first unavailable symbol
            // but ToNumber should return NaN if a string contains unavailable symbols
            if (code < 48 || code > maxCode) return NaN;
          } return parseInt(digits, radix);
        }
      } return +it;
    };
    
    if (!$Number(' 0o1') || !$Number('0b1') || $Number('+0x1')) {
      $Number = function Number(value) {
        var it = arguments.length < 1 ? 0 : value;
        var that = this;
        return that instanceof $Number
          // check on 1..constructor(foo) case
          && (BROKEN_COF ? fails(function () { proto.valueOf.call(that); }) : cof(that) != NUMBER)
            ? inheritIfRequired(new Base(toNumber(it)), that, $Number) : toNumber(it);
      };
      for (var keys = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") ? gOPN(Base) : (
        // ES3:
        'MAX_VALUE,MIN_VALUE,NaN,NEGATIVE_INFINITY,POSITIVE_INFINITY,' +
        // ES6 (in case, if modules with ES6 Number statics required before):
        'EPSILON,isFinite,isInteger,isNaN,isSafeInteger,MAX_SAFE_INTEGER,' +
        'MIN_SAFE_INTEGER,parseFloat,parseInt,isInteger'
      ).split(','), j = 0, key; keys.length > j; j++) {
        if (has(Base, key = keys[j]) && !has($Number, key)) {
          dP($Number, key, gOPD(Base, key));
        }
      }
      $Number.prototype = proto;
      proto.constructor = $Number;
      __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js")(global, NUMBER, $Number);
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.epsilon.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.epsilon.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.1.2.1 Number.EPSILON
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Number', { EPSILON: Math.pow(2, -52) });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.is-finite.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.is-finite.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.1.2.2 Number.isFinite(number)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var _isFinite = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").isFinite;
    
    $export($export.S, 'Number', {
      isFinite: function isFinite(it) {
        return typeof it == 'number' && _isFinite(it);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.is-integer.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.is-integer.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.1.2.3 Number.isInteger(number)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Number', { isInteger: __webpack_require__(/*! ./_is-integer */ "./node_modules/core-js/modules/_is-integer.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.is-nan.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.is-nan.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.1.2.4 Number.isNaN(number)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Number', {
      isNaN: function isNaN(number) {
        // eslint-disable-next-line no-self-compare
        return number != number;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.is-safe-integer.js":
    /*!********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.is-safe-integer.js ***!
      \********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.1.2.5 Number.isSafeInteger(number)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var isInteger = __webpack_require__(/*! ./_is-integer */ "./node_modules/core-js/modules/_is-integer.js");
    var abs = Math.abs;
    
    $export($export.S, 'Number', {
      isSafeInteger: function isSafeInteger(number) {
        return isInteger(number) && abs(number) <= 0x1fffffffffffff;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.max-safe-integer.js":
    /*!*********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.max-safe-integer.js ***!
      \*********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.1.2.6 Number.MAX_SAFE_INTEGER
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Number', { MAX_SAFE_INTEGER: 0x1fffffffffffff });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.min-safe-integer.js":
    /*!*********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.min-safe-integer.js ***!
      \*********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 20.1.2.10 Number.MIN_SAFE_INTEGER
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Number', { MIN_SAFE_INTEGER: -0x1fffffffffffff });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.parse-float.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.parse-float.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $parseFloat = __webpack_require__(/*! ./_parse-float */ "./node_modules/core-js/modules/_parse-float.js");
    // 20.1.2.12 Number.parseFloat(string)
    $export($export.S + $export.F * (Number.parseFloat != $parseFloat), 'Number', { parseFloat: $parseFloat });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.parse-int.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.parse-int.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $parseInt = __webpack_require__(/*! ./_parse-int */ "./node_modules/core-js/modules/_parse-int.js");
    // 20.1.2.13 Number.parseInt(string, radix)
    $export($export.S + $export.F * (Number.parseInt != $parseInt), 'Number', { parseInt: $parseInt });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.to-fixed.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.to-fixed.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var aNumberValue = __webpack_require__(/*! ./_a-number-value */ "./node_modules/core-js/modules/_a-number-value.js");
    var repeat = __webpack_require__(/*! ./_string-repeat */ "./node_modules/core-js/modules/_string-repeat.js");
    var $toFixed = 1.0.toFixed;
    var floor = Math.floor;
    var data = [0, 0, 0, 0, 0, 0];
    var ERROR = 'Number.toFixed: incorrect invocation!';
    var ZERO = '0';
    
    var multiply = function (n, c) {
      var i = -1;
      var c2 = c;
      while (++i < 6) {
        c2 += n * data[i];
        data[i] = c2 % 1e7;
        c2 = floor(c2 / 1e7);
      }
    };
    var divide = function (n) {
      var i = 6;
      var c = 0;
      while (--i >= 0) {
        c += data[i];
        data[i] = floor(c / n);
        c = (c % n) * 1e7;
      }
    };
    var numToString = function () {
      var i = 6;
      var s = '';
      while (--i >= 0) {
        if (s !== '' || i === 0 || data[i] !== 0) {
          var t = String(data[i]);
          s = s === '' ? t : s + repeat.call(ZERO, 7 - t.length) + t;
        }
      } return s;
    };
    var pow = function (x, n, acc) {
      return n === 0 ? acc : n % 2 === 1 ? pow(x, n - 1, acc * x) : pow(x * x, n / 2, acc);
    };
    var log = function (x) {
      var n = 0;
      var x2 = x;
      while (x2 >= 4096) {
        n += 12;
        x2 /= 4096;
      }
      while (x2 >= 2) {
        n += 1;
        x2 /= 2;
      } return n;
    };
    
    $export($export.P + $export.F * (!!$toFixed && (
      0.00008.toFixed(3) !== '0.000' ||
      0.9.toFixed(0) !== '1' ||
      1.255.toFixed(2) !== '1.25' ||
      1000000000000000128.0.toFixed(0) !== '1000000000000000128'
    ) || !__webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      // V8 ~ Android 4.3-
      $toFixed.call({});
    })), 'Number', {
      toFixed: function toFixed(fractionDigits) {
        var x = aNumberValue(this, ERROR);
        var f = toInteger(fractionDigits);
        var s = '';
        var m = ZERO;
        var e, z, j, k;
        if (f < 0 || f > 20) throw RangeError(ERROR);
        // eslint-disable-next-line no-self-compare
        if (x != x) return 'NaN';
        if (x <= -1e21 || x >= 1e21) return String(x);
        if (x < 0) {
          s = '-';
          x = -x;
        }
        if (x > 1e-21) {
          e = log(x * pow(2, 69, 1)) - 69;
          z = e < 0 ? x * pow(2, -e, 1) : x / pow(2, e, 1);
          z *= 0x10000000000000;
          e = 52 - e;
          if (e > 0) {
            multiply(0, z);
            j = f;
            while (j >= 7) {
              multiply(1e7, 0);
              j -= 7;
            }
            multiply(pow(10, j, 1), 0);
            j = e - 1;
            while (j >= 23) {
              divide(1 << 23);
              j -= 23;
            }
            divide(1 << j);
            multiply(1, 1);
            divide(2);
            m = numToString();
          } else {
            multiply(0, z);
            multiply(1 << -e, 0);
            m = numToString() + repeat.call(ZERO, f);
          }
        }
        if (f > 0) {
          k = m.length;
          m = s + (k <= f ? '0.' + repeat.call(ZERO, f - k) + m : m.slice(0, k - f) + '.' + m.slice(k - f));
        } else {
          m = s + m;
        } return m;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.number.to-precision.js":
    /*!*****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.number.to-precision.js ***!
      \*****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var aNumberValue = __webpack_require__(/*! ./_a-number-value */ "./node_modules/core-js/modules/_a-number-value.js");
    var $toPrecision = 1.0.toPrecision;
    
    $export($export.P + $export.F * ($fails(function () {
      // IE7-
      return $toPrecision.call(1, undefined) !== '1';
    }) || !$fails(function () {
      // V8 ~ Android 4.3-
      $toPrecision.call({});
    })), 'Number', {
      toPrecision: function toPrecision(precision) {
        var that = aNumberValue(this, 'Number#toPrecision: incorrect invocation!');
        return precision === undefined ? $toPrecision.call(that) : $toPrecision.call(that, precision);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.assign.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.assign.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.3.1 Object.assign(target, source)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S + $export.F, 'Object', { assign: __webpack_require__(/*! ./_object-assign */ "./node_modules/core-js/modules/_object-assign.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.create.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.create.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    // 19.1.2.2 / 15.2.3.5 Object.create(O [, Properties])
    $export($export.S, 'Object', { create: __webpack_require__(/*! ./_object-create */ "./node_modules/core-js/modules/_object-create.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.define-properties.js":
    /*!**********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.define-properties.js ***!
      \**********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    // 19.1.2.3 / 15.2.3.7 Object.defineProperties(O, Properties)
    $export($export.S + $export.F * !__webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js"), 'Object', { defineProperties: __webpack_require__(/*! ./_object-dps */ "./node_modules/core-js/modules/_object-dps.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.define-property.js":
    /*!********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.define-property.js ***!
      \********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    // 19.1.2.4 / 15.2.3.6 Object.defineProperty(O, P, Attributes)
    $export($export.S + $export.F * !__webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js"), 'Object', { defineProperty: __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.freeze.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.freeze.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.5 Object.freeze(O)
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var meta = __webpack_require__(/*! ./_meta */ "./node_modules/core-js/modules/_meta.js").onFreeze;
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('freeze', function ($freeze) {
      return function freeze(it) {
        return $freeze && isObject(it) ? $freeze(meta(it)) : it;
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.get-own-property-descriptor.js":
    /*!********************************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.get-own-property-descriptor.js ***!
      \********************************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var $getOwnPropertyDescriptor = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js").f;
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('getOwnPropertyDescriptor', function () {
      return function getOwnPropertyDescriptor(it, key) {
        return $getOwnPropertyDescriptor(toIObject(it), key);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.get-own-property-names.js":
    /*!***************************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.get-own-property-names.js ***!
      \***************************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.7 Object.getOwnPropertyNames(O)
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('getOwnPropertyNames', function () {
      return __webpack_require__(/*! ./_object-gopn-ext */ "./node_modules/core-js/modules/_object-gopn-ext.js").f;
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.get-prototype-of.js":
    /*!*********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.get-prototype-of.js ***!
      \*********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.9 Object.getPrototypeOf(O)
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var $getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('getPrototypeOf', function () {
      return function getPrototypeOf(it) {
        return $getPrototypeOf(toObject(it));
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.is-extensible.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.is-extensible.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.11 Object.isExtensible(O)
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('isExtensible', function ($isExtensible) {
      return function isExtensible(it) {
        return isObject(it) ? $isExtensible ? $isExtensible(it) : true : false;
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.is-frozen.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.is-frozen.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.12 Object.isFrozen(O)
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('isFrozen', function ($isFrozen) {
      return function isFrozen(it) {
        return isObject(it) ? $isFrozen ? $isFrozen(it) : false : true;
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.is-sealed.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.is-sealed.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.13 Object.isSealed(O)
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('isSealed', function ($isSealed) {
      return function isSealed(it) {
        return isObject(it) ? $isSealed ? $isSealed(it) : false : true;
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.is.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.is.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.3.10 Object.is(value1, value2)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    $export($export.S, 'Object', { is: __webpack_require__(/*! ./_same-value */ "./node_modules/core-js/modules/_same-value.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.keys.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.keys.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.14 Object.keys(O)
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var $keys = __webpack_require__(/*! ./_object-keys */ "./node_modules/core-js/modules/_object-keys.js");
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('keys', function () {
      return function keys(it) {
        return $keys(toObject(it));
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.prevent-extensions.js":
    /*!***********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.prevent-extensions.js ***!
      \***********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.15 Object.preventExtensions(O)
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var meta = __webpack_require__(/*! ./_meta */ "./node_modules/core-js/modules/_meta.js").onFreeze;
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('preventExtensions', function ($preventExtensions) {
      return function preventExtensions(it) {
        return $preventExtensions && isObject(it) ? $preventExtensions(meta(it)) : it;
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.seal.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.seal.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.2.17 Object.seal(O)
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var meta = __webpack_require__(/*! ./_meta */ "./node_modules/core-js/modules/_meta.js").onFreeze;
    
    __webpack_require__(/*! ./_object-sap */ "./node_modules/core-js/modules/_object-sap.js")('seal', function ($seal) {
      return function seal(it) {
        return $seal && isObject(it) ? $seal(meta(it)) : it;
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.set-prototype-of.js":
    /*!*********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.set-prototype-of.js ***!
      \*********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 19.1.3.19 Object.setPrototypeOf(O, proto)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    $export($export.S, 'Object', { setPrototypeOf: __webpack_require__(/*! ./_set-proto */ "./node_modules/core-js/modules/_set-proto.js").set });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.object.to-string.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.object.to-string.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 19.1.3.6 Object.prototype.toString()
    var classof = __webpack_require__(/*! ./_classof */ "./node_modules/core-js/modules/_classof.js");
    var test = {};
    test[__webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('toStringTag')] = 'z';
    if (test + '' != '[object z]') {
      __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js")(Object.prototype, 'toString', function toString() {
        return '[object ' + classof(this) + ']';
      }, true);
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.parse-float.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.parse-float.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $parseFloat = __webpack_require__(/*! ./_parse-float */ "./node_modules/core-js/modules/_parse-float.js");
    // 18.2.4 parseFloat(string)
    $export($export.G + $export.F * (parseFloat != $parseFloat), { parseFloat: $parseFloat });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.parse-int.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.parse-int.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $parseInt = __webpack_require__(/*! ./_parse-int */ "./node_modules/core-js/modules/_parse-int.js");
    // 18.2.5 parseInt(string, radix)
    $export($export.G + $export.F * (parseInt != $parseInt), { parseInt: $parseInt });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.promise.js":
    /*!*****************************************************!*\
      !*** ./node_modules/core-js/modules/es6.promise.js ***!
      \*****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var LIBRARY = __webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js");
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var ctx = __webpack_require__(/*! ./_ctx */ "./node_modules/core-js/modules/_ctx.js");
    var classof = __webpack_require__(/*! ./_classof */ "./node_modules/core-js/modules/_classof.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var anInstance = __webpack_require__(/*! ./_an-instance */ "./node_modules/core-js/modules/_an-instance.js");
    var forOf = __webpack_require__(/*! ./_for-of */ "./node_modules/core-js/modules/_for-of.js");
    var speciesConstructor = __webpack_require__(/*! ./_species-constructor */ "./node_modules/core-js/modules/_species-constructor.js");
    var task = __webpack_require__(/*! ./_task */ "./node_modules/core-js/modules/_task.js").set;
    var microtask = __webpack_require__(/*! ./_microtask */ "./node_modules/core-js/modules/_microtask.js")();
    var newPromiseCapabilityModule = __webpack_require__(/*! ./_new-promise-capability */ "./node_modules/core-js/modules/_new-promise-capability.js");
    var perform = __webpack_require__(/*! ./_perform */ "./node_modules/core-js/modules/_perform.js");
    var userAgent = __webpack_require__(/*! ./_user-agent */ "./node_modules/core-js/modules/_user-agent.js");
    var promiseResolve = __webpack_require__(/*! ./_promise-resolve */ "./node_modules/core-js/modules/_promise-resolve.js");
    var PROMISE = 'Promise';
    var TypeError = global.TypeError;
    var process = global.process;
    var versions = process && process.versions;
    var v8 = versions && versions.v8 || '';
    var $Promise = global[PROMISE];
    var isNode = classof(process) == 'process';
    var empty = function () { /* empty */ };
    var Internal, newGenericPromiseCapability, OwnPromiseCapability, Wrapper;
    var newPromiseCapability = newGenericPromiseCapability = newPromiseCapabilityModule.f;
    
    var USE_NATIVE = !!function () {
      try {
        // correct subclassing with @@species support
        var promise = $Promise.resolve(1);
        var FakePromise = (promise.constructor = {})[__webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('species')] = function (exec) {
          exec(empty, empty);
        };
        // unhandled rejections tracking support, NodeJS Promise without it fails @@species test
        return (isNode || typeof PromiseRejectionEvent == 'function')
          && promise.then(empty) instanceof FakePromise
          // v8 6.6 (Node 10 and Chrome 66) have a bug with resolving custom thenables
          // https://bugs.chromium.org/p/chromium/issues/detail?id=830565
          // we can't detect it synchronously, so just check versions
          && v8.indexOf('6.6') !== 0
          && userAgent.indexOf('Chrome/66') === -1;
      } catch (e) { /* empty */ }
    }();
    
    // helpers
    var isThenable = function (it) {
      var then;
      return isObject(it) && typeof (then = it.then) == 'function' ? then : false;
    };
    var notify = function (promise, isReject) {
      if (promise._n) return;
      promise._n = true;
      var chain = promise._c;
      microtask(function () {
        var value = promise._v;
        var ok = promise._s == 1;
        var i = 0;
        var run = function (reaction) {
          var handler = ok ? reaction.ok : reaction.fail;
          var resolve = reaction.resolve;
          var reject = reaction.reject;
          var domain = reaction.domain;
          var result, then, exited;
          try {
            if (handler) {
              if (!ok) {
                if (promise._h == 2) onHandleUnhandled(promise);
                promise._h = 1;
              }
              if (handler === true) result = value;
              else {
                if (domain) domain.enter();
                result = handler(value); // may throw
                if (domain) {
                  domain.exit();
                  exited = true;
                }
              }
              if (result === reaction.promise) {
                reject(TypeError('Promise-chain cycle'));
              } else if (then = isThenable(result)) {
                then.call(result, resolve, reject);
              } else resolve(result);
            } else reject(value);
          } catch (e) {
            if (domain && !exited) domain.exit();
            reject(e);
          }
        };
        while (chain.length > i) run(chain[i++]); // variable length - can't use forEach
        promise._c = [];
        promise._n = false;
        if (isReject && !promise._h) onUnhandled(promise);
      });
    };
    var onUnhandled = function (promise) {
      task.call(global, function () {
        var value = promise._v;
        var unhandled = isUnhandled(promise);
        var result, handler, console;
        if (unhandled) {
          result = perform(function () {
            if (isNode) {
              process.emit('unhandledRejection', value, promise);
            } else if (handler = global.onunhandledrejection) {
              handler({ promise: promise, reason: value });
            } else if ((console = global.console) && console.error) {
              console.error('Unhandled promise rejection', value);
            }
          });
          // Browsers should not trigger `rejectionHandled` event if it was handled here, NodeJS - should
          promise._h = isNode || isUnhandled(promise) ? 2 : 1;
        } promise._a = undefined;
        if (unhandled && result.e) throw result.v;
      });
    };
    var isUnhandled = function (promise) {
      return promise._h !== 1 && (promise._a || promise._c).length === 0;
    };
    var onHandleUnhandled = function (promise) {
      task.call(global, function () {
        var handler;
        if (isNode) {
          process.emit('rejectionHandled', promise);
        } else if (handler = global.onrejectionhandled) {
          handler({ promise: promise, reason: promise._v });
        }
      });
    };
    var $reject = function (value) {
      var promise = this;
      if (promise._d) return;
      promise._d = true;
      promise = promise._w || promise; // unwrap
      promise._v = value;
      promise._s = 2;
      if (!promise._a) promise._a = promise._c.slice();
      notify(promise, true);
    };
    var $resolve = function (value) {
      var promise = this;
      var then;
      if (promise._d) return;
      promise._d = true;
      promise = promise._w || promise; // unwrap
      try {
        if (promise === value) throw TypeError("Promise can't be resolved itself");
        if (then = isThenable(value)) {
          microtask(function () {
            var wrapper = { _w: promise, _d: false }; // wrap
            try {
              then.call(value, ctx($resolve, wrapper, 1), ctx($reject, wrapper, 1));
            } catch (e) {
              $reject.call(wrapper, e);
            }
          });
        } else {
          promise._v = value;
          promise._s = 1;
          notify(promise, false);
        }
      } catch (e) {
        $reject.call({ _w: promise, _d: false }, e); // wrap
      }
    };
    
    // constructor polyfill
    if (!USE_NATIVE) {
      // 25.4.3.1 Promise(executor)
      $Promise = function Promise(executor) {
        anInstance(this, $Promise, PROMISE, '_h');
        aFunction(executor);
        Internal.call(this);
        try {
          executor(ctx($resolve, this, 1), ctx($reject, this, 1));
        } catch (err) {
          $reject.call(this, err);
        }
      };
      // eslint-disable-next-line no-unused-vars
      Internal = function Promise(executor) {
        this._c = [];             // <- awaiting reactions
        this._a = undefined;      // <- checked in isUnhandled reactions
        this._s = 0;              // <- state
        this._d = false;          // <- done
        this._v = undefined;      // <- value
        this._h = 0;              // <- rejection state, 0 - default, 1 - handled, 2 - unhandled
        this._n = false;          // <- notify
      };
      Internal.prototype = __webpack_require__(/*! ./_redefine-all */ "./node_modules/core-js/modules/_redefine-all.js")($Promise.prototype, {
        // 25.4.5.3 Promise.prototype.then(onFulfilled, onRejected)
        then: function then(onFulfilled, onRejected) {
          var reaction = newPromiseCapability(speciesConstructor(this, $Promise));
          reaction.ok = typeof onFulfilled == 'function' ? onFulfilled : true;
          reaction.fail = typeof onRejected == 'function' && onRejected;
          reaction.domain = isNode ? process.domain : undefined;
          this._c.push(reaction);
          if (this._a) this._a.push(reaction);
          if (this._s) notify(this, false);
          return reaction.promise;
        },
        // 25.4.5.1 Promise.prototype.catch(onRejected)
        'catch': function (onRejected) {
          return this.then(undefined, onRejected);
        }
      });
      OwnPromiseCapability = function () {
        var promise = new Internal();
        this.promise = promise;
        this.resolve = ctx($resolve, promise, 1);
        this.reject = ctx($reject, promise, 1);
      };
      newPromiseCapabilityModule.f = newPromiseCapability = function (C) {
        return C === $Promise || C === Wrapper
          ? new OwnPromiseCapability(C)
          : newGenericPromiseCapability(C);
      };
    }
    
    $export($export.G + $export.W + $export.F * !USE_NATIVE, { Promise: $Promise });
    __webpack_require__(/*! ./_set-to-string-tag */ "./node_modules/core-js/modules/_set-to-string-tag.js")($Promise, PROMISE);
    __webpack_require__(/*! ./_set-species */ "./node_modules/core-js/modules/_set-species.js")(PROMISE);
    Wrapper = __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js")[PROMISE];
    
    // statics
    $export($export.S + $export.F * !USE_NATIVE, PROMISE, {
      // 25.4.4.5 Promise.reject(r)
      reject: function reject(r) {
        var capability = newPromiseCapability(this);
        var $$reject = capability.reject;
        $$reject(r);
        return capability.promise;
      }
    });
    $export($export.S + $export.F * (LIBRARY || !USE_NATIVE), PROMISE, {
      // 25.4.4.6 Promise.resolve(x)
      resolve: function resolve(x) {
        return promiseResolve(LIBRARY && this === Wrapper ? $Promise : this, x);
      }
    });
    $export($export.S + $export.F * !(USE_NATIVE && __webpack_require__(/*! ./_iter-detect */ "./node_modules/core-js/modules/_iter-detect.js")(function (iter) {
      $Promise.all(iter)['catch'](empty);
    })), PROMISE, {
      // 25.4.4.1 Promise.all(iterable)
      all: function all(iterable) {
        var C = this;
        var capability = newPromiseCapability(C);
        var resolve = capability.resolve;
        var reject = capability.reject;
        var result = perform(function () {
          var values = [];
          var index = 0;
          var remaining = 1;
          forOf(iterable, false, function (promise) {
            var $index = index++;
            var alreadyCalled = false;
            values.push(undefined);
            remaining++;
            C.resolve(promise).then(function (value) {
              if (alreadyCalled) return;
              alreadyCalled = true;
              values[$index] = value;
              --remaining || resolve(values);
            }, reject);
          });
          --remaining || resolve(values);
        });
        if (result.e) reject(result.v);
        return capability.promise;
      },
      // 25.4.4.4 Promise.race(iterable)
      race: function race(iterable) {
        var C = this;
        var capability = newPromiseCapability(C);
        var reject = capability.reject;
        var result = perform(function () {
          forOf(iterable, false, function (promise) {
            C.resolve(promise).then(capability.resolve, reject);
          });
        });
        if (result.e) reject(result.v);
        return capability.promise;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.apply.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.apply.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.1 Reflect.apply(target, thisArgument, argumentsList)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var rApply = (__webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").Reflect || {}).apply;
    var fApply = Function.apply;
    // MS Edge argumentsList argument is optional
    $export($export.S + $export.F * !__webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      rApply(function () { /* empty */ });
    }), 'Reflect', {
      apply: function apply(target, thisArgument, argumentsList) {
        var T = aFunction(target);
        var L = anObject(argumentsList);
        return rApply ? rApply(T, thisArgument, L) : fApply.call(T, thisArgument, L);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.construct.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.construct.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.2 Reflect.construct(target, argumentsList [, newTarget])
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var create = __webpack_require__(/*! ./_object-create */ "./node_modules/core-js/modules/_object-create.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var bind = __webpack_require__(/*! ./_bind */ "./node_modules/core-js/modules/_bind.js");
    var rConstruct = (__webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").Reflect || {}).construct;
    
    // MS Edge supports only 2 arguments and argumentsList argument is optional
    // FF Nightly sets third argument as `new.target`, but does not create `this` from it
    var NEW_TARGET_BUG = fails(function () {
      function F() { /* empty */ }
      return !(rConstruct(function () { /* empty */ }, [], F) instanceof F);
    });
    var ARGS_BUG = !fails(function () {
      rConstruct(function () { /* empty */ });
    });
    
    $export($export.S + $export.F * (NEW_TARGET_BUG || ARGS_BUG), 'Reflect', {
      construct: function construct(Target, args /* , newTarget */) {
        aFunction(Target);
        anObject(args);
        var newTarget = arguments.length < 3 ? Target : aFunction(arguments[2]);
        if (ARGS_BUG && !NEW_TARGET_BUG) return rConstruct(Target, args, newTarget);
        if (Target == newTarget) {
          // w/o altered newTarget, optimization for 0-4 arguments
          switch (args.length) {
            case 0: return new Target();
            case 1: return new Target(args[0]);
            case 2: return new Target(args[0], args[1]);
            case 3: return new Target(args[0], args[1], args[2]);
            case 4: return new Target(args[0], args[1], args[2], args[3]);
          }
          // w/o altered newTarget, lot of arguments case
          var $args = [null];
          $args.push.apply($args, args);
          return new (bind.apply(Target, $args))();
        }
        // with altered newTarget, not support built-in constructors
        var proto = newTarget.prototype;
        var instance = create(isObject(proto) ? proto : Object.prototype);
        var result = Function.apply.call(Target, instance, args);
        return isObject(result) ? result : instance;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.define-property.js":
    /*!*********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.define-property.js ***!
      \*********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.3 Reflect.defineProperty(target, propertyKey, attributes)
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    
    // MS Edge has broken Reflect.defineProperty - throwing instead of returning false
    $export($export.S + $export.F * __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      // eslint-disable-next-line no-undef
      Reflect.defineProperty(dP.f({}, 1, { value: 1 }), 1, { value: 2 });
    }), 'Reflect', {
      defineProperty: function defineProperty(target, propertyKey, attributes) {
        anObject(target);
        propertyKey = toPrimitive(propertyKey, true);
        anObject(attributes);
        try {
          dP.f(target, propertyKey, attributes);
          return true;
        } catch (e) {
          return false;
        }
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.delete-property.js":
    /*!*********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.delete-property.js ***!
      \*********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.4 Reflect.deleteProperty(target, propertyKey)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var gOPD = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js").f;
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    
    $export($export.S, 'Reflect', {
      deleteProperty: function deleteProperty(target, propertyKey) {
        var desc = gOPD(anObject(target), propertyKey);
        return desc && !desc.configurable ? false : delete target[propertyKey];
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.enumerate.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.enumerate.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 26.1.5 Reflect.enumerate(target)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var Enumerate = function (iterated) {
      this._t = anObject(iterated); // target
      this._i = 0;                  // next index
      var keys = this._k = [];      // keys
      var key;
      for (key in iterated) keys.push(key);
    };
    __webpack_require__(/*! ./_iter-create */ "./node_modules/core-js/modules/_iter-create.js")(Enumerate, 'Object', function () {
      var that = this;
      var keys = that._k;
      var key;
      do {
        if (that._i >= keys.length) return { value: undefined, done: true };
      } while (!((key = keys[that._i++]) in that._t));
      return { value: key, done: false };
    });
    
    $export($export.S, 'Reflect', {
      enumerate: function enumerate(target) {
        return new Enumerate(target);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.get-own-property-descriptor.js":
    /*!*********************************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.get-own-property-descriptor.js ***!
      \*********************************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.7 Reflect.getOwnPropertyDescriptor(target, propertyKey)
    var gOPD = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    
    $export($export.S, 'Reflect', {
      getOwnPropertyDescriptor: function getOwnPropertyDescriptor(target, propertyKey) {
        return gOPD.f(anObject(target), propertyKey);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.get-prototype-of.js":
    /*!**********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.get-prototype-of.js ***!
      \**********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.8 Reflect.getPrototypeOf(target)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var getProto = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    
    $export($export.S, 'Reflect', {
      getPrototypeOf: function getPrototypeOf(target) {
        return getProto(anObject(target));
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.get.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.get.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.6 Reflect.get(target, propertyKey [, receiver])
    var gOPD = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    
    function get(target, propertyKey /* , receiver */) {
      var receiver = arguments.length < 3 ? target : arguments[2];
      var desc, proto;
      if (anObject(target) === receiver) return target[propertyKey];
      if (desc = gOPD.f(target, propertyKey)) return has(desc, 'value')
        ? desc.value
        : desc.get !== undefined
          ? desc.get.call(receiver)
          : undefined;
      if (isObject(proto = getPrototypeOf(target))) return get(proto, propertyKey, receiver);
    }
    
    $export($export.S, 'Reflect', { get: get });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.has.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.has.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.9 Reflect.has(target, propertyKey)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Reflect', {
      has: function has(target, propertyKey) {
        return propertyKey in target;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.is-extensible.js":
    /*!*******************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.is-extensible.js ***!
      \*******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.10 Reflect.isExtensible(target)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var $isExtensible = Object.isExtensible;
    
    $export($export.S, 'Reflect', {
      isExtensible: function isExtensible(target) {
        anObject(target);
        return $isExtensible ? $isExtensible(target) : true;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.own-keys.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.own-keys.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.11 Reflect.ownKeys(target)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Reflect', { ownKeys: __webpack_require__(/*! ./_own-keys */ "./node_modules/core-js/modules/_own-keys.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.prevent-extensions.js":
    /*!************************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.prevent-extensions.js ***!
      \************************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.12 Reflect.preventExtensions(target)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var $preventExtensions = Object.preventExtensions;
    
    $export($export.S, 'Reflect', {
      preventExtensions: function preventExtensions(target) {
        anObject(target);
        try {
          if ($preventExtensions) $preventExtensions(target);
          return true;
        } catch (e) {
          return false;
        }
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.set-prototype-of.js":
    /*!**********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.set-prototype-of.js ***!
      \**********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.14 Reflect.setPrototypeOf(target, proto)
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var setProto = __webpack_require__(/*! ./_set-proto */ "./node_modules/core-js/modules/_set-proto.js");
    
    if (setProto) $export($export.S, 'Reflect', {
      setPrototypeOf: function setPrototypeOf(target, proto) {
        setProto.check(target, proto);
        try {
          setProto.set(target, proto);
          return true;
        } catch (e) {
          return false;
        }
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.reflect.set.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.reflect.set.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 26.1.13 Reflect.set(target, propertyKey, V [, receiver])
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    var gOPD = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var createDesc = __webpack_require__(/*! ./_property-desc */ "./node_modules/core-js/modules/_property-desc.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    
    function set(target, propertyKey, V /* , receiver */) {
      var receiver = arguments.length < 4 ? target : arguments[3];
      var ownDesc = gOPD.f(anObject(target), propertyKey);
      var existingDescriptor, proto;
      if (!ownDesc) {
        if (isObject(proto = getPrototypeOf(target))) {
          return set(proto, propertyKey, V, receiver);
        }
        ownDesc = createDesc(0);
      }
      if (has(ownDesc, 'value')) {
        if (ownDesc.writable === false || !isObject(receiver)) return false;
        if (existingDescriptor = gOPD.f(receiver, propertyKey)) {
          if (existingDescriptor.get || existingDescriptor.set || existingDescriptor.writable === false) return false;
          existingDescriptor.value = V;
          dP.f(receiver, propertyKey, existingDescriptor);
        } else dP.f(receiver, propertyKey, createDesc(0, V));
        return true;
      }
      return ownDesc.set === undefined ? false : (ownDesc.set.call(receiver, V), true);
    }
    
    $export($export.S, 'Reflect', { set: set });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.regexp.constructor.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.regexp.constructor.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var inheritIfRequired = __webpack_require__(/*! ./_inherit-if-required */ "./node_modules/core-js/modules/_inherit-if-required.js");
    var dP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f;
    var gOPN = __webpack_require__(/*! ./_object-gopn */ "./node_modules/core-js/modules/_object-gopn.js").f;
    var isRegExp = __webpack_require__(/*! ./_is-regexp */ "./node_modules/core-js/modules/_is-regexp.js");
    var $flags = __webpack_require__(/*! ./_flags */ "./node_modules/core-js/modules/_flags.js");
    var $RegExp = global.RegExp;
    var Base = $RegExp;
    var proto = $RegExp.prototype;
    var re1 = /a/g;
    var re2 = /a/g;
    // "new" creates a new object, old webkit buggy here
    var CORRECT_NEW = new $RegExp(re1) !== re1;
    
    if (__webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") && (!CORRECT_NEW || __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      re2[__webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('match')] = false;
      // RegExp constructor can alter flags and IsRegExp works correct with @@match
      return $RegExp(re1) != re1 || $RegExp(re2) == re2 || $RegExp(re1, 'i') != '/a/i';
    }))) {
      $RegExp = function RegExp(p, f) {
        var tiRE = this instanceof $RegExp;
        var piRE = isRegExp(p);
        var fiU = f === undefined;
        return !tiRE && piRE && p.constructor === $RegExp && fiU ? p
          : inheritIfRequired(CORRECT_NEW
            ? new Base(piRE && !fiU ? p.source : p, f)
            : Base((piRE = p instanceof $RegExp) ? p.source : p, piRE && fiU ? $flags.call(p) : f)
          , tiRE ? this : proto, $RegExp);
      };
      var proxy = function (key) {
        key in $RegExp || dP($RegExp, key, {
          configurable: true,
          get: function () { return Base[key]; },
          set: function (it) { Base[key] = it; }
        });
      };
      for (var keys = gOPN(Base), i = 0; keys.length > i;) proxy(keys[i++]);
      proto.constructor = $RegExp;
      $RegExp.prototype = proto;
      __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js")(global, 'RegExp', $RegExp);
    }
    
    __webpack_require__(/*! ./_set-species */ "./node_modules/core-js/modules/_set-species.js")('RegExp');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.regexp.exec.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.regexp.exec.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var regexpExec = __webpack_require__(/*! ./_regexp-exec */ "./node_modules/core-js/modules/_regexp-exec.js");
    __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js")({
      target: 'RegExp',
      proto: true,
      forced: regexpExec !== /./.exec
    }, {
      exec: regexpExec
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.regexp.flags.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.regexp.flags.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // 21.2.5.3 get RegExp.prototype.flags()
    if (__webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") && /./g.flags != 'g') __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js").f(RegExp.prototype, 'flags', {
      configurable: true,
      get: __webpack_require__(/*! ./_flags */ "./node_modules/core-js/modules/_flags.js")
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.regexp.match.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.regexp.match.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var advanceStringIndex = __webpack_require__(/*! ./_advance-string-index */ "./node_modules/core-js/modules/_advance-string-index.js");
    var regExpExec = __webpack_require__(/*! ./_regexp-exec-abstract */ "./node_modules/core-js/modules/_regexp-exec-abstract.js");
    
    // @@match logic
    __webpack_require__(/*! ./_fix-re-wks */ "./node_modules/core-js/modules/_fix-re-wks.js")('match', 1, function (defined, MATCH, $match, maybeCallNative) {
      return [
        // `String.prototype.match` method
        // https://tc39.github.io/ecma262/#sec-string.prototype.match
        function match(regexp) {
          var O = defined(this);
          var fn = regexp == undefined ? undefined : regexp[MATCH];
          return fn !== undefined ? fn.call(regexp, O) : new RegExp(regexp)[MATCH](String(O));
        },
        // `RegExp.prototype[@@match]` method
        // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@match
        function (regexp) {
          var res = maybeCallNative($match, regexp, this);
          if (res.done) return res.value;
          var rx = anObject(regexp);
          var S = String(this);
          if (!rx.global) return regExpExec(rx, S);
          var fullUnicode = rx.unicode;
          rx.lastIndex = 0;
          var A = [];
          var n = 0;
          var result;
          while ((result = regExpExec(rx, S)) !== null) {
            var matchStr = String(result[0]);
            A[n] = matchStr;
            if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
            n++;
          }
          return n === 0 ? null : A;
        }
      ];
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.regexp.replace.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.regexp.replace.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var advanceStringIndex = __webpack_require__(/*! ./_advance-string-index */ "./node_modules/core-js/modules/_advance-string-index.js");
    var regExpExec = __webpack_require__(/*! ./_regexp-exec-abstract */ "./node_modules/core-js/modules/_regexp-exec-abstract.js");
    var max = Math.max;
    var min = Math.min;
    var floor = Math.floor;
    var SUBSTITUTION_SYMBOLS = /\$([$&`']|\d\d?|<[^>]*>)/g;
    var SUBSTITUTION_SYMBOLS_NO_NAMED = /\$([$&`']|\d\d?)/g;
    
    var maybeToString = function (it) {
      return it === undefined ? it : String(it);
    };
    
    // @@replace logic
    __webpack_require__(/*! ./_fix-re-wks */ "./node_modules/core-js/modules/_fix-re-wks.js")('replace', 2, function (defined, REPLACE, $replace, maybeCallNative) {
      return [
        // `String.prototype.replace` method
        // https://tc39.github.io/ecma262/#sec-string.prototype.replace
        function replace(searchValue, replaceValue) {
          var O = defined(this);
          var fn = searchValue == undefined ? undefined : searchValue[REPLACE];
          return fn !== undefined
            ? fn.call(searchValue, O, replaceValue)
            : $replace.call(String(O), searchValue, replaceValue);
        },
        // `RegExp.prototype[@@replace]` method
        // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@replace
        function (regexp, replaceValue) {
          var res = maybeCallNative($replace, regexp, this, replaceValue);
          if (res.done) return res.value;
    
          var rx = anObject(regexp);
          var S = String(this);
          var functionalReplace = typeof replaceValue === 'function';
          if (!functionalReplace) replaceValue = String(replaceValue);
          var global = rx.global;
          if (global) {
            var fullUnicode = rx.unicode;
            rx.lastIndex = 0;
          }
          var results = [];
          while (true) {
            var result = regExpExec(rx, S);
            if (result === null) break;
            results.push(result);
            if (!global) break;
            var matchStr = String(result[0]);
            if (matchStr === '') rx.lastIndex = advanceStringIndex(S, toLength(rx.lastIndex), fullUnicode);
          }
          var accumulatedResult = '';
          var nextSourcePosition = 0;
          for (var i = 0; i < results.length; i++) {
            result = results[i];
            var matched = String(result[0]);
            var position = max(min(toInteger(result.index), S.length), 0);
            var captures = [];
            // NOTE: This is equivalent to
            //   captures = result.slice(1).map(maybeToString)
            // but for some reason `nativeSlice.call(result, 1, result.length)` (called in
            // the slice polyfill when slicing native arrays) "doesn't work" in safari 9 and
            // causes a crash (https://pastebin.com/N21QzeQA) when trying to debug it.
            for (var j = 1; j < result.length; j++) captures.push(maybeToString(result[j]));
            var namedCaptures = result.groups;
            if (functionalReplace) {
              var replacerArgs = [matched].concat(captures, position, S);
              if (namedCaptures !== undefined) replacerArgs.push(namedCaptures);
              var replacement = String(replaceValue.apply(undefined, replacerArgs));
            } else {
              replacement = getSubstitution(matched, S, position, captures, namedCaptures, replaceValue);
            }
            if (position >= nextSourcePosition) {
              accumulatedResult += S.slice(nextSourcePosition, position) + replacement;
              nextSourcePosition = position + matched.length;
            }
          }
          return accumulatedResult + S.slice(nextSourcePosition);
        }
      ];
    
        // https://tc39.github.io/ecma262/#sec-getsubstitution
      function getSubstitution(matched, str, position, captures, namedCaptures, replacement) {
        var tailPos = position + matched.length;
        var m = captures.length;
        var symbols = SUBSTITUTION_SYMBOLS_NO_NAMED;
        if (namedCaptures !== undefined) {
          namedCaptures = toObject(namedCaptures);
          symbols = SUBSTITUTION_SYMBOLS;
        }
        return $replace.call(replacement, symbols, function (match, ch) {
          var capture;
          switch (ch.charAt(0)) {
            case '$': return '$';
            case '&': return matched;
            case '`': return str.slice(0, position);
            case "'": return str.slice(tailPos);
            case '<':
              capture = namedCaptures[ch.slice(1, -1)];
              break;
            default: // \d\d?
              var n = +ch;
              if (n === 0) return match;
              if (n > m) {
                var f = floor(n / 10);
                if (f === 0) return match;
                if (f <= m) return captures[f - 1] === undefined ? ch.charAt(1) : captures[f - 1] + ch.charAt(1);
                return match;
              }
              capture = captures[n - 1];
          }
          return capture === undefined ? '' : capture;
        });
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.regexp.search.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.regexp.search.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var sameValue = __webpack_require__(/*! ./_same-value */ "./node_modules/core-js/modules/_same-value.js");
    var regExpExec = __webpack_require__(/*! ./_regexp-exec-abstract */ "./node_modules/core-js/modules/_regexp-exec-abstract.js");
    
    // @@search logic
    __webpack_require__(/*! ./_fix-re-wks */ "./node_modules/core-js/modules/_fix-re-wks.js")('search', 1, function (defined, SEARCH, $search, maybeCallNative) {
      return [
        // `String.prototype.search` method
        // https://tc39.github.io/ecma262/#sec-string.prototype.search
        function search(regexp) {
          var O = defined(this);
          var fn = regexp == undefined ? undefined : regexp[SEARCH];
          return fn !== undefined ? fn.call(regexp, O) : new RegExp(regexp)[SEARCH](String(O));
        },
        // `RegExp.prototype[@@search]` method
        // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@search
        function (regexp) {
          var res = maybeCallNative($search, regexp, this);
          if (res.done) return res.value;
          var rx = anObject(regexp);
          var S = String(this);
          var previousLastIndex = rx.lastIndex;
          if (!sameValue(previousLastIndex, 0)) rx.lastIndex = 0;
          var result = regExpExec(rx, S);
          if (!sameValue(rx.lastIndex, previousLastIndex)) rx.lastIndex = previousLastIndex;
          return result === null ? -1 : result.index;
        }
      ];
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.regexp.split.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.regexp.split.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    var isRegExp = __webpack_require__(/*! ./_is-regexp */ "./node_modules/core-js/modules/_is-regexp.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var speciesConstructor = __webpack_require__(/*! ./_species-constructor */ "./node_modules/core-js/modules/_species-constructor.js");
    var advanceStringIndex = __webpack_require__(/*! ./_advance-string-index */ "./node_modules/core-js/modules/_advance-string-index.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var callRegExpExec = __webpack_require__(/*! ./_regexp-exec-abstract */ "./node_modules/core-js/modules/_regexp-exec-abstract.js");
    var regexpExec = __webpack_require__(/*! ./_regexp-exec */ "./node_modules/core-js/modules/_regexp-exec.js");
    var fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var $min = Math.min;
    var $push = [].push;
    var $SPLIT = 'split';
    var LENGTH = 'length';
    var LAST_INDEX = 'lastIndex';
    var MAX_UINT32 = 0xffffffff;
    
    // babel-minify transpiles RegExp('x', 'y') -> /x/y and it causes SyntaxError
    var SUPPORTS_Y = !fails(function () { RegExp(MAX_UINT32, 'y'); });
    
    // @@split logic
    __webpack_require__(/*! ./_fix-re-wks */ "./node_modules/core-js/modules/_fix-re-wks.js")('split', 2, function (defined, SPLIT, $split, maybeCallNative) {
      var internalSplit;
      if (
        'abbc'[$SPLIT](/(b)*/)[1] == 'c' ||
        'test'[$SPLIT](/(?:)/, -1)[LENGTH] != 4 ||
        'ab'[$SPLIT](/(?:ab)*/)[LENGTH] != 2 ||
        '.'[$SPLIT](/(.?)(.?)/)[LENGTH] != 4 ||
        '.'[$SPLIT](/()()/)[LENGTH] > 1 ||
        ''[$SPLIT](/.?/)[LENGTH]
      ) {
        // based on es5-shim implementation, need to rework it
        internalSplit = function (separator, limit) {
          var string = String(this);
          if (separator === undefined && limit === 0) return [];
          // If `separator` is not a regex, use native split
          if (!isRegExp(separator)) return $split.call(string, separator, limit);
          var output = [];
          var flags = (separator.ignoreCase ? 'i' : '') +
                      (separator.multiline ? 'm' : '') +
                      (separator.unicode ? 'u' : '') +
                      (separator.sticky ? 'y' : '');
          var lastLastIndex = 0;
          var splitLimit = limit === undefined ? MAX_UINT32 : limit >>> 0;
          // Make `global` and avoid `lastIndex` issues by working with a copy
          var separatorCopy = new RegExp(separator.source, flags + 'g');
          var match, lastIndex, lastLength;
          while (match = regexpExec.call(separatorCopy, string)) {
            lastIndex = separatorCopy[LAST_INDEX];
            if (lastIndex > lastLastIndex) {
              output.push(string.slice(lastLastIndex, match.index));
              if (match[LENGTH] > 1 && match.index < string[LENGTH]) $push.apply(output, match.slice(1));
              lastLength = match[0][LENGTH];
              lastLastIndex = lastIndex;
              if (output[LENGTH] >= splitLimit) break;
            }
            if (separatorCopy[LAST_INDEX] === match.index) separatorCopy[LAST_INDEX]++; // Avoid an infinite loop
          }
          if (lastLastIndex === string[LENGTH]) {
            if (lastLength || !separatorCopy.test('')) output.push('');
          } else output.push(string.slice(lastLastIndex));
          return output[LENGTH] > splitLimit ? output.slice(0, splitLimit) : output;
        };
      // Chakra, V8
      } else if ('0'[$SPLIT](undefined, 0)[LENGTH]) {
        internalSplit = function (separator, limit) {
          return separator === undefined && limit === 0 ? [] : $split.call(this, separator, limit);
        };
      } else {
        internalSplit = $split;
      }
    
      return [
        // `String.prototype.split` method
        // https://tc39.github.io/ecma262/#sec-string.prototype.split
        function split(separator, limit) {
          var O = defined(this);
          var splitter = separator == undefined ? undefined : separator[SPLIT];
          return splitter !== undefined
            ? splitter.call(separator, O, limit)
            : internalSplit.call(String(O), separator, limit);
        },
        // `RegExp.prototype[@@split]` method
        // https://tc39.github.io/ecma262/#sec-regexp.prototype-@@split
        //
        // NOTE: This cannot be properly polyfilled in engines that don't support
        // the 'y' flag.
        function (regexp, limit) {
          var res = maybeCallNative(internalSplit, regexp, this, limit, internalSplit !== $split);
          if (res.done) return res.value;
    
          var rx = anObject(regexp);
          var S = String(this);
          var C = speciesConstructor(rx, RegExp);
    
          var unicodeMatching = rx.unicode;
          var flags = (rx.ignoreCase ? 'i' : '') +
                      (rx.multiline ? 'm' : '') +
                      (rx.unicode ? 'u' : '') +
                      (SUPPORTS_Y ? 'y' : 'g');
    
          // ^(? + rx + ) is needed, in combination with some S slicing, to
          // simulate the 'y' flag.
          var splitter = new C(SUPPORTS_Y ? rx : '^(?:' + rx.source + ')', flags);
          var lim = limit === undefined ? MAX_UINT32 : limit >>> 0;
          if (lim === 0) return [];
          if (S.length === 0) return callRegExpExec(splitter, S) === null ? [S] : [];
          var p = 0;
          var q = 0;
          var A = [];
          while (q < S.length) {
            splitter.lastIndex = SUPPORTS_Y ? q : 0;
            var z = callRegExpExec(splitter, SUPPORTS_Y ? S : S.slice(q));
            var e;
            if (
              z === null ||
              (e = $min(toLength(splitter.lastIndex + (SUPPORTS_Y ? 0 : q)), S.length)) === p
            ) {
              q = advanceStringIndex(S, q, unicodeMatching);
            } else {
              A.push(S.slice(p, q));
              if (A.length === lim) return A;
              for (var i = 1; i <= z.length - 1; i++) {
                A.push(z[i]);
                if (A.length === lim) return A;
              }
              q = p = e;
            }
          }
          A.push(S.slice(p));
          return A;
        }
      ];
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.regexp.to-string.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.regexp.to-string.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    __webpack_require__(/*! ./es6.regexp.flags */ "./node_modules/core-js/modules/es6.regexp.flags.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var $flags = __webpack_require__(/*! ./_flags */ "./node_modules/core-js/modules/_flags.js");
    var DESCRIPTORS = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js");
    var TO_STRING = 'toString';
    var $toString = /./[TO_STRING];
    
    var define = function (fn) {
      __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js")(RegExp.prototype, TO_STRING, fn, true);
    };
    
    // 21.2.5.14 RegExp.prototype.toString()
    if (__webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () { return $toString.call({ source: 'a', flags: 'b' }) != '/a/b'; })) {
      define(function toString() {
        var R = anObject(this);
        return '/'.concat(R.source, '/',
          'flags' in R ? R.flags : !DESCRIPTORS && R instanceof RegExp ? $flags.call(R) : undefined);
      });
    // FF44- RegExp#toString has a wrong name
    } else if ($toString.name != TO_STRING) {
      define(function toString() {
        return $toString.call(this);
      });
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.set.js":
    /*!*************************************************!*\
      !*** ./node_modules/core-js/modules/es6.set.js ***!
      \*************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var strong = __webpack_require__(/*! ./_collection-strong */ "./node_modules/core-js/modules/_collection-strong.js");
    var validate = __webpack_require__(/*! ./_validate-collection */ "./node_modules/core-js/modules/_validate-collection.js");
    var SET = 'Set';
    
    // 23.2 Set Objects
    module.exports = __webpack_require__(/*! ./_collection */ "./node_modules/core-js/modules/_collection.js")(SET, function (get) {
      return function Set() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
    }, {
      // 23.2.3.1 Set.prototype.add(value)
      add: function add(value) {
        return strong.def(validate(this, SET), value = value === 0 ? 0 : value, value);
      }
    }, strong);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.anchor.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.anchor.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.2 String.prototype.anchor(name)
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('anchor', function (createHTML) {
      return function anchor(name) {
        return createHTML(this, 'a', 'name', name);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.big.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.big.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.3 String.prototype.big()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('big', function (createHTML) {
      return function big() {
        return createHTML(this, 'big', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.blink.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.blink.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.4 String.prototype.blink()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('blink', function (createHTML) {
      return function blink() {
        return createHTML(this, 'blink', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.bold.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.bold.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.5 String.prototype.bold()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('bold', function (createHTML) {
      return function bold() {
        return createHTML(this, 'b', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.code-point-at.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.code-point-at.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $at = __webpack_require__(/*! ./_string-at */ "./node_modules/core-js/modules/_string-at.js")(false);
    $export($export.P, 'String', {
      // 21.1.3.3 String.prototype.codePointAt(pos)
      codePointAt: function codePointAt(pos) {
        return $at(this, pos);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.ends-with.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.ends-with.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    // 21.1.3.6 String.prototype.endsWith(searchString [, endPosition])
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var context = __webpack_require__(/*! ./_string-context */ "./node_modules/core-js/modules/_string-context.js");
    var ENDS_WITH = 'endsWith';
    var $endsWith = ''[ENDS_WITH];
    
    $export($export.P + $export.F * __webpack_require__(/*! ./_fails-is-regexp */ "./node_modules/core-js/modules/_fails-is-regexp.js")(ENDS_WITH), 'String', {
      endsWith: function endsWith(searchString /* , endPosition = @length */) {
        var that = context(this, searchString, ENDS_WITH);
        var endPosition = arguments.length > 1 ? arguments[1] : undefined;
        var len = toLength(that.length);
        var end = endPosition === undefined ? len : Math.min(toLength(endPosition), len);
        var search = String(searchString);
        return $endsWith
          ? $endsWith.call(that, search, end)
          : that.slice(end - search.length, end) === search;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.fixed.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.fixed.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.6 String.prototype.fixed()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('fixed', function (createHTML) {
      return function fixed() {
        return createHTML(this, 'tt', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.fontcolor.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.fontcolor.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.7 String.prototype.fontcolor(color)
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('fontcolor', function (createHTML) {
      return function fontcolor(color) {
        return createHTML(this, 'font', 'color', color);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.fontsize.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.fontsize.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.8 String.prototype.fontsize(size)
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('fontsize', function (createHTML) {
      return function fontsize(size) {
        return createHTML(this, 'font', 'size', size);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.from-code-point.js":
    /*!********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.from-code-point.js ***!
      \********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toAbsoluteIndex = __webpack_require__(/*! ./_to-absolute-index */ "./node_modules/core-js/modules/_to-absolute-index.js");
    var fromCharCode = String.fromCharCode;
    var $fromCodePoint = String.fromCodePoint;
    
    // length should be 1, old FF problem
    $export($export.S + $export.F * (!!$fromCodePoint && $fromCodePoint.length != 1), 'String', {
      // 21.1.2.2 String.fromCodePoint(...codePoints)
      fromCodePoint: function fromCodePoint(x) { // eslint-disable-line no-unused-vars
        var res = [];
        var aLen = arguments.length;
        var i = 0;
        var code;
        while (aLen > i) {
          code = +arguments[i++];
          if (toAbsoluteIndex(code, 0x10ffff) !== code) throw RangeError(code + ' is not a valid code point');
          res.push(code < 0x10000
            ? fromCharCode(code)
            : fromCharCode(((code -= 0x10000) >> 10) + 0xd800, code % 0x400 + 0xdc00)
          );
        } return res.join('');
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.includes.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.includes.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    // 21.1.3.7 String.prototype.includes(searchString, position = 0)
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var context = __webpack_require__(/*! ./_string-context */ "./node_modules/core-js/modules/_string-context.js");
    var INCLUDES = 'includes';
    
    $export($export.P + $export.F * __webpack_require__(/*! ./_fails-is-regexp */ "./node_modules/core-js/modules/_fails-is-regexp.js")(INCLUDES), 'String', {
      includes: function includes(searchString /* , position = 0 */) {
        return !!~context(this, searchString, INCLUDES)
          .indexOf(searchString, arguments.length > 1 ? arguments[1] : undefined);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.italics.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.italics.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.9 String.prototype.italics()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('italics', function (createHTML) {
      return function italics() {
        return createHTML(this, 'i', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.iterator.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.iterator.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $at = __webpack_require__(/*! ./_string-at */ "./node_modules/core-js/modules/_string-at.js")(true);
    
    // 21.1.3.27 String.prototype[@@iterator]()
    __webpack_require__(/*! ./_iter-define */ "./node_modules/core-js/modules/_iter-define.js")(String, 'String', function (iterated) {
      this._t = String(iterated); // target
      this._i = 0;                // next index
    // 21.1.5.2.1 %StringIteratorPrototype%.next()
    }, function () {
      var O = this._t;
      var index = this._i;
      var point;
      if (index >= O.length) return { value: undefined, done: true };
      point = $at(O, index);
      this._i += point.length;
      return { value: point, done: false };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.link.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.link.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.10 String.prototype.link(url)
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('link', function (createHTML) {
      return function link(url) {
        return createHTML(this, 'a', 'href', url);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.raw.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.raw.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    
    $export($export.S, 'String', {
      // 21.1.2.4 String.raw(callSite, ...substitutions)
      raw: function raw(callSite) {
        var tpl = toIObject(callSite.raw);
        var len = toLength(tpl.length);
        var aLen = arguments.length;
        var res = [];
        var i = 0;
        while (len > i) {
          res.push(String(tpl[i++]));
          if (i < aLen) res.push(String(arguments[i]));
        } return res.join('');
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.repeat.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.repeat.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.P, 'String', {
      // 21.1.3.13 String.prototype.repeat(count)
      repeat: __webpack_require__(/*! ./_string-repeat */ "./node_modules/core-js/modules/_string-repeat.js")
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.small.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.small.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.11 String.prototype.small()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('small', function (createHTML) {
      return function small() {
        return createHTML(this, 'small', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.starts-with.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.starts-with.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    // 21.1.3.18 String.prototype.startsWith(searchString [, position ])
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var context = __webpack_require__(/*! ./_string-context */ "./node_modules/core-js/modules/_string-context.js");
    var STARTS_WITH = 'startsWith';
    var $startsWith = ''[STARTS_WITH];
    
    $export($export.P + $export.F * __webpack_require__(/*! ./_fails-is-regexp */ "./node_modules/core-js/modules/_fails-is-regexp.js")(STARTS_WITH), 'String', {
      startsWith: function startsWith(searchString /* , position = 0 */) {
        var that = context(this, searchString, STARTS_WITH);
        var index = toLength(Math.min(arguments.length > 1 ? arguments[1] : undefined, that.length));
        var search = String(searchString);
        return $startsWith
          ? $startsWith.call(that, search, index)
          : that.slice(index, index + search.length) === search;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.strike.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.strike.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.12 String.prototype.strike()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('strike', function (createHTML) {
      return function strike() {
        return createHTML(this, 'strike', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.sub.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.sub.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.13 String.prototype.sub()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('sub', function (createHTML) {
      return function sub() {
        return createHTML(this, 'sub', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.sup.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.sup.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // B.2.3.14 String.prototype.sup()
    __webpack_require__(/*! ./_string-html */ "./node_modules/core-js/modules/_string-html.js")('sup', function (createHTML) {
      return function sup() {
        return createHTML(this, 'sup', '', '');
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.string.trim.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es6.string.trim.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // 21.1.3.25 String.prototype.trim()
    __webpack_require__(/*! ./_string-trim */ "./node_modules/core-js/modules/_string-trim.js")('trim', function ($trim) {
      return function trim() {
        return $trim(this, 3);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.symbol.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/es6.symbol.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // ECMAScript 6 symbols shim
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var has = __webpack_require__(/*! ./_has */ "./node_modules/core-js/modules/_has.js");
    var DESCRIPTORS = __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var redefine = __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js");
    var META = __webpack_require__(/*! ./_meta */ "./node_modules/core-js/modules/_meta.js").KEY;
    var $fails = __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js");
    var shared = __webpack_require__(/*! ./_shared */ "./node_modules/core-js/modules/_shared.js");
    var setToStringTag = __webpack_require__(/*! ./_set-to-string-tag */ "./node_modules/core-js/modules/_set-to-string-tag.js");
    var uid = __webpack_require__(/*! ./_uid */ "./node_modules/core-js/modules/_uid.js");
    var wks = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js");
    var wksExt = __webpack_require__(/*! ./_wks-ext */ "./node_modules/core-js/modules/_wks-ext.js");
    var wksDefine = __webpack_require__(/*! ./_wks-define */ "./node_modules/core-js/modules/_wks-define.js");
    var enumKeys = __webpack_require__(/*! ./_enum-keys */ "./node_modules/core-js/modules/_enum-keys.js");
    var isArray = __webpack_require__(/*! ./_is-array */ "./node_modules/core-js/modules/_is-array.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    var createDesc = __webpack_require__(/*! ./_property-desc */ "./node_modules/core-js/modules/_property-desc.js");
    var _create = __webpack_require__(/*! ./_object-create */ "./node_modules/core-js/modules/_object-create.js");
    var gOPNExt = __webpack_require__(/*! ./_object-gopn-ext */ "./node_modules/core-js/modules/_object-gopn-ext.js");
    var $GOPD = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js");
    var $DP = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    var $keys = __webpack_require__(/*! ./_object-keys */ "./node_modules/core-js/modules/_object-keys.js");
    var gOPD = $GOPD.f;
    var dP = $DP.f;
    var gOPN = gOPNExt.f;
    var $Symbol = global.Symbol;
    var $JSON = global.JSON;
    var _stringify = $JSON && $JSON.stringify;
    var PROTOTYPE = 'prototype';
    var HIDDEN = wks('_hidden');
    var TO_PRIMITIVE = wks('toPrimitive');
    var isEnum = {}.propertyIsEnumerable;
    var SymbolRegistry = shared('symbol-registry');
    var AllSymbols = shared('symbols');
    var OPSymbols = shared('op-symbols');
    var ObjectProto = Object[PROTOTYPE];
    var USE_NATIVE = typeof $Symbol == 'function';
    var QObject = global.QObject;
    // Don't use setters in Qt Script, https://github.com/zloirock/core-js/issues/173
    var setter = !QObject || !QObject[PROTOTYPE] || !QObject[PROTOTYPE].findChild;
    
    // fallback for old Android, https://code.google.com/p/v8/issues/detail?id=687
    var setSymbolDesc = DESCRIPTORS && $fails(function () {
      return _create(dP({}, 'a', {
        get: function () { return dP(this, 'a', { value: 7 }).a; }
      })).a != 7;
    }) ? function (it, key, D) {
      var protoDesc = gOPD(ObjectProto, key);
      if (protoDesc) delete ObjectProto[key];
      dP(it, key, D);
      if (protoDesc && it !== ObjectProto) dP(ObjectProto, key, protoDesc);
    } : dP;
    
    var wrap = function (tag) {
      var sym = AllSymbols[tag] = _create($Symbol[PROTOTYPE]);
      sym._k = tag;
      return sym;
    };
    
    var isSymbol = USE_NATIVE && typeof $Symbol.iterator == 'symbol' ? function (it) {
      return typeof it == 'symbol';
    } : function (it) {
      return it instanceof $Symbol;
    };
    
    var $defineProperty = function defineProperty(it, key, D) {
      if (it === ObjectProto) $defineProperty(OPSymbols, key, D);
      anObject(it);
      key = toPrimitive(key, true);
      anObject(D);
      if (has(AllSymbols, key)) {
        if (!D.enumerable) {
          if (!has(it, HIDDEN)) dP(it, HIDDEN, createDesc(1, {}));
          it[HIDDEN][key] = true;
        } else {
          if (has(it, HIDDEN) && it[HIDDEN][key]) it[HIDDEN][key] = false;
          D = _create(D, { enumerable: createDesc(0, false) });
        } return setSymbolDesc(it, key, D);
      } return dP(it, key, D);
    };
    var $defineProperties = function defineProperties(it, P) {
      anObject(it);
      var keys = enumKeys(P = toIObject(P));
      var i = 0;
      var l = keys.length;
      var key;
      while (l > i) $defineProperty(it, key = keys[i++], P[key]);
      return it;
    };
    var $create = function create(it, P) {
      return P === undefined ? _create(it) : $defineProperties(_create(it), P);
    };
    var $propertyIsEnumerable = function propertyIsEnumerable(key) {
      var E = isEnum.call(this, key = toPrimitive(key, true));
      if (this === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return false;
      return E || !has(this, key) || !has(AllSymbols, key) || has(this, HIDDEN) && this[HIDDEN][key] ? E : true;
    };
    var $getOwnPropertyDescriptor = function getOwnPropertyDescriptor(it, key) {
      it = toIObject(it);
      key = toPrimitive(key, true);
      if (it === ObjectProto && has(AllSymbols, key) && !has(OPSymbols, key)) return;
      var D = gOPD(it, key);
      if (D && has(AllSymbols, key) && !(has(it, HIDDEN) && it[HIDDEN][key])) D.enumerable = true;
      return D;
    };
    var $getOwnPropertyNames = function getOwnPropertyNames(it) {
      var names = gOPN(toIObject(it));
      var result = [];
      var i = 0;
      var key;
      while (names.length > i) {
        if (!has(AllSymbols, key = names[i++]) && key != HIDDEN && key != META) result.push(key);
      } return result;
    };
    var $getOwnPropertySymbols = function getOwnPropertySymbols(it) {
      var IS_OP = it === ObjectProto;
      var names = gOPN(IS_OP ? OPSymbols : toIObject(it));
      var result = [];
      var i = 0;
      var key;
      while (names.length > i) {
        if (has(AllSymbols, key = names[i++]) && (IS_OP ? has(ObjectProto, key) : true)) result.push(AllSymbols[key]);
      } return result;
    };
    
    // 19.4.1.1 Symbol([description])
    if (!USE_NATIVE) {
      $Symbol = function Symbol() {
        if (this instanceof $Symbol) throw TypeError('Symbol is not a constructor!');
        var tag = uid(arguments.length > 0 ? arguments[0] : undefined);
        var $set = function (value) {
          if (this === ObjectProto) $set.call(OPSymbols, value);
          if (has(this, HIDDEN) && has(this[HIDDEN], tag)) this[HIDDEN][tag] = false;
          setSymbolDesc(this, tag, createDesc(1, value));
        };
        if (DESCRIPTORS && setter) setSymbolDesc(ObjectProto, tag, { configurable: true, set: $set });
        return wrap(tag);
      };
      redefine($Symbol[PROTOTYPE], 'toString', function toString() {
        return this._k;
      });
    
      $GOPD.f = $getOwnPropertyDescriptor;
      $DP.f = $defineProperty;
      __webpack_require__(/*! ./_object-gopn */ "./node_modules/core-js/modules/_object-gopn.js").f = gOPNExt.f = $getOwnPropertyNames;
      __webpack_require__(/*! ./_object-pie */ "./node_modules/core-js/modules/_object-pie.js").f = $propertyIsEnumerable;
      __webpack_require__(/*! ./_object-gops */ "./node_modules/core-js/modules/_object-gops.js").f = $getOwnPropertySymbols;
    
      if (DESCRIPTORS && !__webpack_require__(/*! ./_library */ "./node_modules/core-js/modules/_library.js")) {
        redefine(ObjectProto, 'propertyIsEnumerable', $propertyIsEnumerable, true);
      }
    
      wksExt.f = function (name) {
        return wrap(wks(name));
      };
    }
    
    $export($export.G + $export.W + $export.F * !USE_NATIVE, { Symbol: $Symbol });
    
    for (var es6Symbols = (
      // 19.4.2.2, 19.4.2.3, 19.4.2.4, 19.4.2.6, 19.4.2.8, 19.4.2.9, 19.4.2.10, 19.4.2.11, 19.4.2.12, 19.4.2.13, 19.4.2.14
      'hasInstance,isConcatSpreadable,iterator,match,replace,search,species,split,toPrimitive,toStringTag,unscopables'
    ).split(','), j = 0; es6Symbols.length > j;)wks(es6Symbols[j++]);
    
    for (var wellKnownSymbols = $keys(wks.store), k = 0; wellKnownSymbols.length > k;) wksDefine(wellKnownSymbols[k++]);
    
    $export($export.S + $export.F * !USE_NATIVE, 'Symbol', {
      // 19.4.2.1 Symbol.for(key)
      'for': function (key) {
        return has(SymbolRegistry, key += '')
          ? SymbolRegistry[key]
          : SymbolRegistry[key] = $Symbol(key);
      },
      // 19.4.2.5 Symbol.keyFor(sym)
      keyFor: function keyFor(sym) {
        if (!isSymbol(sym)) throw TypeError(sym + ' is not a symbol!');
        for (var key in SymbolRegistry) if (SymbolRegistry[key] === sym) return key;
      },
      useSetter: function () { setter = true; },
      useSimple: function () { setter = false; }
    });
    
    $export($export.S + $export.F * !USE_NATIVE, 'Object', {
      // 19.1.2.2 Object.create(O [, Properties])
      create: $create,
      // 19.1.2.4 Object.defineProperty(O, P, Attributes)
      defineProperty: $defineProperty,
      // 19.1.2.3 Object.defineProperties(O, Properties)
      defineProperties: $defineProperties,
      // 19.1.2.6 Object.getOwnPropertyDescriptor(O, P)
      getOwnPropertyDescriptor: $getOwnPropertyDescriptor,
      // 19.1.2.7 Object.getOwnPropertyNames(O)
      getOwnPropertyNames: $getOwnPropertyNames,
      // 19.1.2.8 Object.getOwnPropertySymbols(O)
      getOwnPropertySymbols: $getOwnPropertySymbols
    });
    
    // 24.3.2 JSON.stringify(value [, replacer [, space]])
    $JSON && $export($export.S + $export.F * (!USE_NATIVE || $fails(function () {
      var S = $Symbol();
      // MS Edge converts symbol values to JSON as {}
      // WebKit converts symbol values to JSON as null
      // V8 throws on boxed symbols
      return _stringify([S]) != '[null]' || _stringify({ a: S }) != '{}' || _stringify(Object(S)) != '{}';
    })), 'JSON', {
      stringify: function stringify(it) {
        var args = [it];
        var i = 1;
        var replacer, $replacer;
        while (arguments.length > i) args.push(arguments[i++]);
        $replacer = replacer = args[1];
        if (!isObject(replacer) && it === undefined || isSymbol(it)) return; // IE8 returns string on undefined
        if (!isArray(replacer)) replacer = function (key, value) {
          if (typeof $replacer == 'function') value = $replacer.call(this, key, value);
          if (!isSymbol(value)) return value;
        };
        args[1] = replacer;
        return _stringify.apply($JSON, args);
      }
    });
    
    // 19.4.3.4 Symbol.prototype[@@toPrimitive](hint)
    $Symbol[PROTOTYPE][TO_PRIMITIVE] || __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js")($Symbol[PROTOTYPE], TO_PRIMITIVE, $Symbol[PROTOTYPE].valueOf);
    // 19.4.3.5 Symbol.prototype[@@toStringTag]
    setToStringTag($Symbol, 'Symbol');
    // 20.2.1.9 Math[@@toStringTag]
    setToStringTag(Math, 'Math', true);
    // 24.3.3 JSON[@@toStringTag]
    setToStringTag(global.JSON, 'JSON', true);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.array-buffer.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.array-buffer.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $typed = __webpack_require__(/*! ./_typed */ "./node_modules/core-js/modules/_typed.js");
    var buffer = __webpack_require__(/*! ./_typed-buffer */ "./node_modules/core-js/modules/_typed-buffer.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var toAbsoluteIndex = __webpack_require__(/*! ./_to-absolute-index */ "./node_modules/core-js/modules/_to-absolute-index.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var ArrayBuffer = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").ArrayBuffer;
    var speciesConstructor = __webpack_require__(/*! ./_species-constructor */ "./node_modules/core-js/modules/_species-constructor.js");
    var $ArrayBuffer = buffer.ArrayBuffer;
    var $DataView = buffer.DataView;
    var $isView = $typed.ABV && ArrayBuffer.isView;
    var $slice = $ArrayBuffer.prototype.slice;
    var VIEW = $typed.VIEW;
    var ARRAY_BUFFER = 'ArrayBuffer';
    
    $export($export.G + $export.W + $export.F * (ArrayBuffer !== $ArrayBuffer), { ArrayBuffer: $ArrayBuffer });
    
    $export($export.S + $export.F * !$typed.CONSTR, ARRAY_BUFFER, {
      // 24.1.3.1 ArrayBuffer.isView(arg)
      isView: function isView(it) {
        return $isView && $isView(it) || isObject(it) && VIEW in it;
      }
    });
    
    $export($export.P + $export.U + $export.F * __webpack_require__(/*! ./_fails */ "./node_modules/core-js/modules/_fails.js")(function () {
      return !new $ArrayBuffer(2).slice(1, undefined).byteLength;
    }), ARRAY_BUFFER, {
      // 24.1.4.3 ArrayBuffer.prototype.slice(start, end)
      slice: function slice(start, end) {
        if ($slice !== undefined && end === undefined) return $slice.call(anObject(this), start); // FF fix
        var len = anObject(this).byteLength;
        var first = toAbsoluteIndex(start, len);
        var fin = toAbsoluteIndex(end === undefined ? len : end, len);
        var result = new (speciesConstructor(this, $ArrayBuffer))(toLength(fin - first));
        var viewS = new $DataView(this);
        var viewT = new $DataView(result);
        var index = 0;
        while (first < fin) {
          viewT.setUint8(index++, viewS.getUint8(first++));
        } return result;
      }
    });
    
    __webpack_require__(/*! ./_set-species */ "./node_modules/core-js/modules/_set-species.js")(ARRAY_BUFFER);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.data-view.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.data-view.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    $export($export.G + $export.W + $export.F * !__webpack_require__(/*! ./_typed */ "./node_modules/core-js/modules/_typed.js").ABV, {
      DataView: __webpack_require__(/*! ./_typed-buffer */ "./node_modules/core-js/modules/_typed-buffer.js").DataView
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.float32-array.js":
    /*!*****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.float32-array.js ***!
      \*****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Float32', 4, function (init) {
      return function Float32Array(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.float64-array.js":
    /*!*****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.float64-array.js ***!
      \*****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Float64', 8, function (init) {
      return function Float64Array(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.int16-array.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.int16-array.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Int16', 2, function (init) {
      return function Int16Array(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.int32-array.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.int32-array.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Int32', 4, function (init) {
      return function Int32Array(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.int8-array.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.int8-array.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Int8', 1, function (init) {
      return function Int8Array(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.uint16-array.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.uint16-array.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Uint16', 2, function (init) {
      return function Uint16Array(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.uint32-array.js":
    /*!****************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.uint32-array.js ***!
      \****************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Uint32', 4, function (init) {
      return function Uint32Array(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.uint8-array.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.uint8-array.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Uint8', 1, function (init) {
      return function Uint8Array(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.typed.uint8-clamped-array.js":
    /*!***********************************************************************!*\
      !*** ./node_modules/core-js/modules/es6.typed.uint8-clamped-array.js ***!
      \***********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_typed-array */ "./node_modules/core-js/modules/_typed-array.js")('Uint8', 1, function (init) {
      return function Uint8ClampedArray(data, byteOffset, length) {
        return init(this, data, byteOffset, length);
      };
    }, true);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.weak-map.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.weak-map.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var each = __webpack_require__(/*! ./_array-methods */ "./node_modules/core-js/modules/_array-methods.js")(0);
    var redefine = __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js");
    var meta = __webpack_require__(/*! ./_meta */ "./node_modules/core-js/modules/_meta.js");
    var assign = __webpack_require__(/*! ./_object-assign */ "./node_modules/core-js/modules/_object-assign.js");
    var weak = __webpack_require__(/*! ./_collection-weak */ "./node_modules/core-js/modules/_collection-weak.js");
    var isObject = __webpack_require__(/*! ./_is-object */ "./node_modules/core-js/modules/_is-object.js");
    var validate = __webpack_require__(/*! ./_validate-collection */ "./node_modules/core-js/modules/_validate-collection.js");
    var NATIVE_WEAK_MAP = __webpack_require__(/*! ./_validate-collection */ "./node_modules/core-js/modules/_validate-collection.js");
    var IS_IE11 = !global.ActiveXObject && 'ActiveXObject' in global;
    var WEAK_MAP = 'WeakMap';
    var getWeak = meta.getWeak;
    var isExtensible = Object.isExtensible;
    var uncaughtFrozenStore = weak.ufstore;
    var InternalMap;
    
    var wrapper = function (get) {
      return function WeakMap() {
        return get(this, arguments.length > 0 ? arguments[0] : undefined);
      };
    };
    
    var methods = {
      // 23.3.3.3 WeakMap.prototype.get(key)
      get: function get(key) {
        if (isObject(key)) {
          var data = getWeak(key);
          if (data === true) return uncaughtFrozenStore(validate(this, WEAK_MAP)).get(key);
          return data ? data[this._i] : undefined;
        }
      },
      // 23.3.3.5 WeakMap.prototype.set(key, value)
      set: function set(key, value) {
        return weak.def(validate(this, WEAK_MAP), key, value);
      }
    };
    
    // 23.3 WeakMap Objects
    var $WeakMap = module.exports = __webpack_require__(/*! ./_collection */ "./node_modules/core-js/modules/_collection.js")(WEAK_MAP, wrapper, methods, weak, true, true);
    
    // IE11 WeakMap frozen keys fix
    if (NATIVE_WEAK_MAP && IS_IE11) {
      InternalMap = weak.getConstructor(wrapper, WEAK_MAP);
      assign(InternalMap.prototype, methods);
      meta.NEED = true;
      each(['delete', 'has', 'get', 'set'], function (key) {
        var proto = $WeakMap.prototype;
        var method = proto[key];
        redefine(proto, key, function (a, b) {
          // store frozen objects on internal weakmap shim
          if (isObject(a) && !isExtensible(a)) {
            if (!this._f) this._f = new InternalMap();
            var result = this._f[key](a, b);
            return key == 'set' ? this : result;
          // store all the rest on native weakmap
          } return method.call(this, a, b);
        });
      });
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es6.weak-set.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/es6.weak-set.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var weak = __webpack_require__(/*! ./_collection-weak */ "./node_modules/core-js/modules/_collection-weak.js");
    var validate = __webpack_require__(/*! ./_validate-collection */ "./node_modules/core-js/modules/_validate-collection.js");
    var WEAK_SET = 'WeakSet';
    
    // 23.4 WeakSet Objects
    __webpack_require__(/*! ./_collection */ "./node_modules/core-js/modules/_collection.js")(WEAK_SET, function (get) {
      return function WeakSet() { return get(this, arguments.length > 0 ? arguments[0] : undefined); };
    }, {
      // 23.4.3.1 WeakSet.prototype.add(value)
      add: function add(value) {
        return weak.def(validate(this, WEAK_SET), value, true);
      }
    }, weak, false, true);
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.array.flat-map.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.array.flat-map.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatMap
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var flattenIntoArray = __webpack_require__(/*! ./_flatten-into-array */ "./node_modules/core-js/modules/_flatten-into-array.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var arraySpeciesCreate = __webpack_require__(/*! ./_array-species-create */ "./node_modules/core-js/modules/_array-species-create.js");
    
    $export($export.P, 'Array', {
      flatMap: function flatMap(callbackfn /* , thisArg */) {
        var O = toObject(this);
        var sourceLen, A;
        aFunction(callbackfn);
        sourceLen = toLength(O.length);
        A = arraySpeciesCreate(O, 0);
        flattenIntoArray(A, O, O, sourceLen, 0, 1, callbackfn, arguments[1]);
        return A;
      }
    });
    
    __webpack_require__(/*! ./_add-to-unscopables */ "./node_modules/core-js/modules/_add-to-unscopables.js")('flatMap');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.array.flatten.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.array.flatten.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://tc39.github.io/proposal-flatMap/#sec-Array.prototype.flatten
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var flattenIntoArray = __webpack_require__(/*! ./_flatten-into-array */ "./node_modules/core-js/modules/_flatten-into-array.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var toInteger = __webpack_require__(/*! ./_to-integer */ "./node_modules/core-js/modules/_to-integer.js");
    var arraySpeciesCreate = __webpack_require__(/*! ./_array-species-create */ "./node_modules/core-js/modules/_array-species-create.js");
    
    $export($export.P, 'Array', {
      flatten: function flatten(/* depthArg = 1 */) {
        var depthArg = arguments[0];
        var O = toObject(this);
        var sourceLen = toLength(O.length);
        var A = arraySpeciesCreate(O, 0);
        flattenIntoArray(A, O, O, sourceLen, 0, depthArg === undefined ? 1 : toInteger(depthArg));
        return A;
      }
    });
    
    __webpack_require__(/*! ./_add-to-unscopables */ "./node_modules/core-js/modules/_add-to-unscopables.js")('flatten');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.array.includes.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.array.includes.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://github.com/tc39/Array.prototype.includes
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $includes = __webpack_require__(/*! ./_array-includes */ "./node_modules/core-js/modules/_array-includes.js")(true);
    
    $export($export.P, 'Array', {
      includes: function includes(el /* , fromIndex = 0 */) {
        return $includes(this, el, arguments.length > 1 ? arguments[1] : undefined);
      }
    });
    
    __webpack_require__(/*! ./_add-to-unscopables */ "./node_modules/core-js/modules/_add-to-unscopables.js")('includes');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.asap.js":
    /*!**************************************************!*\
      !*** ./node_modules/core-js/modules/es7.asap.js ***!
      \**************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/rwaldron/tc39-notes/blob/master/es6/2014-09/sept-25.md#510-globalasap-for-enqueuing-a-microtask
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var microtask = __webpack_require__(/*! ./_microtask */ "./node_modules/core-js/modules/_microtask.js")();
    var process = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js").process;
    var isNode = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js")(process) == 'process';
    
    $export($export.G, {
      asap: function asap(fn) {
        var domain = isNode && process.domain;
        microtask(domain ? domain.bind(fn) : fn);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.error.is-error.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.error.is-error.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/ljharb/proposal-is-error
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var cof = __webpack_require__(/*! ./_cof */ "./node_modules/core-js/modules/_cof.js");
    
    $export($export.S, 'Error', {
      isError: function isError(it) {
        return cof(it) === 'Error';
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.global.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/es7.global.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/tc39/proposal-global
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.G, { global: __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.map.from.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/es7.map.from.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/proposal-setmap-offrom/#sec-map.from
    __webpack_require__(/*! ./_set-collection-from */ "./node_modules/core-js/modules/_set-collection-from.js")('Map');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.map.of.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/es7.map.of.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/proposal-setmap-offrom/#sec-map.of
    __webpack_require__(/*! ./_set-collection-of */ "./node_modules/core-js/modules/_set-collection-of.js")('Map');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.map.to-json.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.map.to-json.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/DavidBruant/Map-Set.prototype.toJSON
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.P + $export.R, 'Map', { toJSON: __webpack_require__(/*! ./_collection-to-json */ "./node_modules/core-js/modules/_collection-to-json.js")('Map') });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.clamp.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.clamp.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://rwaldron.github.io/proposal-math-extensions/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      clamp: function clamp(x, lower, upper) {
        return Math.min(upper, Math.max(lower, x));
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.deg-per-rad.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.deg-per-rad.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://rwaldron.github.io/proposal-math-extensions/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', { DEG_PER_RAD: Math.PI / 180 });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.degrees.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.degrees.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://rwaldron.github.io/proposal-math-extensions/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var RAD_PER_DEG = 180 / Math.PI;
    
    $export($export.S, 'Math', {
      degrees: function degrees(radians) {
        return radians * RAD_PER_DEG;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.fscale.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.fscale.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://rwaldron.github.io/proposal-math-extensions/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var scale = __webpack_require__(/*! ./_math-scale */ "./node_modules/core-js/modules/_math-scale.js");
    var fround = __webpack_require__(/*! ./_math-fround */ "./node_modules/core-js/modules/_math-fround.js");
    
    $export($export.S, 'Math', {
      fscale: function fscale(x, inLow, inHigh, outLow, outHigh) {
        return fround(scale(x, inLow, inHigh, outLow, outHigh));
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.iaddh.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.iaddh.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://gist.github.com/BrendanEich/4294d5c212a6d2254703
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      iaddh: function iaddh(x0, x1, y0, y1) {
        var $x0 = x0 >>> 0;
        var $x1 = x1 >>> 0;
        var $y0 = y0 >>> 0;
        return $x1 + (y1 >>> 0) + (($x0 & $y0 | ($x0 | $y0) & ~($x0 + $y0 >>> 0)) >>> 31) | 0;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.imulh.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.imulh.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://gist.github.com/BrendanEich/4294d5c212a6d2254703
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      imulh: function imulh(u, v) {
        var UINT16 = 0xffff;
        var $u = +u;
        var $v = +v;
        var u0 = $u & UINT16;
        var v0 = $v & UINT16;
        var u1 = $u >> 16;
        var v1 = $v >> 16;
        var t = (u1 * v0 >>> 0) + (u0 * v0 >>> 16);
        return u1 * v1 + (t >> 16) + ((u0 * v1 >>> 0) + (t & UINT16) >> 16);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.isubh.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.isubh.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://gist.github.com/BrendanEich/4294d5c212a6d2254703
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      isubh: function isubh(x0, x1, y0, y1) {
        var $x0 = x0 >>> 0;
        var $x1 = x1 >>> 0;
        var $y0 = y0 >>> 0;
        return $x1 - (y1 >>> 0) - ((~$x0 & $y0 | ~($x0 ^ $y0) & $x0 - $y0 >>> 0) >>> 31) | 0;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.rad-per-deg.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.rad-per-deg.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://rwaldron.github.io/proposal-math-extensions/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', { RAD_PER_DEG: 180 / Math.PI });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.radians.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.radians.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://rwaldron.github.io/proposal-math-extensions/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var DEG_PER_RAD = Math.PI / 180;
    
    $export($export.S, 'Math', {
      radians: function radians(degrees) {
        return degrees * DEG_PER_RAD;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.scale.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.scale.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://rwaldron.github.io/proposal-math-extensions/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', { scale: __webpack_require__(/*! ./_math-scale */ "./node_modules/core-js/modules/_math-scale.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.signbit.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.signbit.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // http://jfbastien.github.io/papers/Math.signbit.html
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', { signbit: function signbit(x) {
      // eslint-disable-next-line no-self-compare
      return (x = +x) != x ? x : x == 0 ? 1 / x == Infinity : x > 0;
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.math.umulh.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.math.umulh.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://gist.github.com/BrendanEich/4294d5c212a6d2254703
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'Math', {
      umulh: function umulh(u, v) {
        var UINT16 = 0xffff;
        var $u = +u;
        var $v = +v;
        var u0 = $u & UINT16;
        var v0 = $v & UINT16;
        var u1 = $u >>> 16;
        var v1 = $v >>> 16;
        var t = (u1 * v0 >>> 0) + (u0 * v0 >>> 16);
        return u1 * v1 + (t >>> 16) + ((u0 * v1 >>> 0) + (t & UINT16) >>> 16);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.object.define-getter.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.object.define-getter.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var $defineProperty = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    
    // B.2.2.2 Object.prototype.__defineGetter__(P, getter)
    __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") && $export($export.P + __webpack_require__(/*! ./_object-forced-pam */ "./node_modules/core-js/modules/_object-forced-pam.js"), 'Object', {
      __defineGetter__: function __defineGetter__(P, getter) {
        $defineProperty.f(toObject(this), P, { get: aFunction(getter), enumerable: true, configurable: true });
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.object.define-setter.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.object.define-setter.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var $defineProperty = __webpack_require__(/*! ./_object-dp */ "./node_modules/core-js/modules/_object-dp.js");
    
    // B.2.2.3 Object.prototype.__defineSetter__(P, setter)
    __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") && $export($export.P + __webpack_require__(/*! ./_object-forced-pam */ "./node_modules/core-js/modules/_object-forced-pam.js"), 'Object', {
      __defineSetter__: function __defineSetter__(P, setter) {
        $defineProperty.f(toObject(this), P, { set: aFunction(setter), enumerable: true, configurable: true });
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.object.entries.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.object.entries.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/tc39/proposal-object-values-entries
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $entries = __webpack_require__(/*! ./_object-to-array */ "./node_modules/core-js/modules/_object-to-array.js")(true);
    
    $export($export.S, 'Object', {
      entries: function entries(it) {
        return $entries(it);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.object.get-own-property-descriptors.js":
    /*!*********************************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.object.get-own-property-descriptors.js ***!
      \*********************************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/tc39/proposal-object-getownpropertydescriptors
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var ownKeys = __webpack_require__(/*! ./_own-keys */ "./node_modules/core-js/modules/_own-keys.js");
    var toIObject = __webpack_require__(/*! ./_to-iobject */ "./node_modules/core-js/modules/_to-iobject.js");
    var gOPD = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js");
    var createProperty = __webpack_require__(/*! ./_create-property */ "./node_modules/core-js/modules/_create-property.js");
    
    $export($export.S, 'Object', {
      getOwnPropertyDescriptors: function getOwnPropertyDescriptors(object) {
        var O = toIObject(object);
        var getDesc = gOPD.f;
        var keys = ownKeys(O);
        var result = {};
        var i = 0;
        var key, desc;
        while (keys.length > i) {
          desc = getDesc(O, key = keys[i++]);
          if (desc !== undefined) createProperty(result, key, desc);
        }
        return result;
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.object.lookup-getter.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.object.lookup-getter.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var getOwnPropertyDescriptor = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js").f;
    
    // B.2.2.4 Object.prototype.__lookupGetter__(P)
    __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") && $export($export.P + __webpack_require__(/*! ./_object-forced-pam */ "./node_modules/core-js/modules/_object-forced-pam.js"), 'Object', {
      __lookupGetter__: function __lookupGetter__(P) {
        var O = toObject(this);
        var K = toPrimitive(P, true);
        var D;
        do {
          if (D = getOwnPropertyDescriptor(O, K)) return D.get;
        } while (O = getPrototypeOf(O));
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.object.lookup-setter.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.object.lookup-setter.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var toObject = __webpack_require__(/*! ./_to-object */ "./node_modules/core-js/modules/_to-object.js");
    var toPrimitive = __webpack_require__(/*! ./_to-primitive */ "./node_modules/core-js/modules/_to-primitive.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var getOwnPropertyDescriptor = __webpack_require__(/*! ./_object-gopd */ "./node_modules/core-js/modules/_object-gopd.js").f;
    
    // B.2.2.5 Object.prototype.__lookupSetter__(P)
    __webpack_require__(/*! ./_descriptors */ "./node_modules/core-js/modules/_descriptors.js") && $export($export.P + __webpack_require__(/*! ./_object-forced-pam */ "./node_modules/core-js/modules/_object-forced-pam.js"), 'Object', {
      __lookupSetter__: function __lookupSetter__(P) {
        var O = toObject(this);
        var K = toPrimitive(P, true);
        var D;
        do {
          if (D = getOwnPropertyDescriptor(O, K)) return D.set;
        } while (O = getPrototypeOf(O));
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.object.values.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.object.values.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/tc39/proposal-object-values-entries
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $values = __webpack_require__(/*! ./_object-to-array */ "./node_modules/core-js/modules/_object-to-array.js")(false);
    
    $export($export.S, 'Object', {
      values: function values(it) {
        return $values(it);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.observable.js":
    /*!********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.observable.js ***!
      \********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://github.com/zenparsing/es-observable
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var core = __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js");
    var microtask = __webpack_require__(/*! ./_microtask */ "./node_modules/core-js/modules/_microtask.js")();
    var OBSERVABLE = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js")('observable');
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var anInstance = __webpack_require__(/*! ./_an-instance */ "./node_modules/core-js/modules/_an-instance.js");
    var redefineAll = __webpack_require__(/*! ./_redefine-all */ "./node_modules/core-js/modules/_redefine-all.js");
    var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
    var forOf = __webpack_require__(/*! ./_for-of */ "./node_modules/core-js/modules/_for-of.js");
    var RETURN = forOf.RETURN;
    
    var getMethod = function (fn) {
      return fn == null ? undefined : aFunction(fn);
    };
    
    var cleanupSubscription = function (subscription) {
      var cleanup = subscription._c;
      if (cleanup) {
        subscription._c = undefined;
        cleanup();
      }
    };
    
    var subscriptionClosed = function (subscription) {
      return subscription._o === undefined;
    };
    
    var closeSubscription = function (subscription) {
      if (!subscriptionClosed(subscription)) {
        subscription._o = undefined;
        cleanupSubscription(subscription);
      }
    };
    
    var Subscription = function (observer, subscriber) {
      anObject(observer);
      this._c = undefined;
      this._o = observer;
      observer = new SubscriptionObserver(this);
      try {
        var cleanup = subscriber(observer);
        var subscription = cleanup;
        if (cleanup != null) {
          if (typeof cleanup.unsubscribe === 'function') cleanup = function () { subscription.unsubscribe(); };
          else aFunction(cleanup);
          this._c = cleanup;
        }
      } catch (e) {
        observer.error(e);
        return;
      } if (subscriptionClosed(this)) cleanupSubscription(this);
    };
    
    Subscription.prototype = redefineAll({}, {
      unsubscribe: function unsubscribe() { closeSubscription(this); }
    });
    
    var SubscriptionObserver = function (subscription) {
      this._s = subscription;
    };
    
    SubscriptionObserver.prototype = redefineAll({}, {
      next: function next(value) {
        var subscription = this._s;
        if (!subscriptionClosed(subscription)) {
          var observer = subscription._o;
          try {
            var m = getMethod(observer.next);
            if (m) return m.call(observer, value);
          } catch (e) {
            try {
              closeSubscription(subscription);
            } finally {
              throw e;
            }
          }
        }
      },
      error: function error(value) {
        var subscription = this._s;
        if (subscriptionClosed(subscription)) throw value;
        var observer = subscription._o;
        subscription._o = undefined;
        try {
          var m = getMethod(observer.error);
          if (!m) throw value;
          value = m.call(observer, value);
        } catch (e) {
          try {
            cleanupSubscription(subscription);
          } finally {
            throw e;
          }
        } cleanupSubscription(subscription);
        return value;
      },
      complete: function complete(value) {
        var subscription = this._s;
        if (!subscriptionClosed(subscription)) {
          var observer = subscription._o;
          subscription._o = undefined;
          try {
            var m = getMethod(observer.complete);
            value = m ? m.call(observer, value) : undefined;
          } catch (e) {
            try {
              cleanupSubscription(subscription);
            } finally {
              throw e;
            }
          } cleanupSubscription(subscription);
          return value;
        }
      }
    });
    
    var $Observable = function Observable(subscriber) {
      anInstance(this, $Observable, 'Observable', '_f')._f = aFunction(subscriber);
    };
    
    redefineAll($Observable.prototype, {
      subscribe: function subscribe(observer) {
        return new Subscription(observer, this._f);
      },
      forEach: function forEach(fn) {
        var that = this;
        return new (core.Promise || global.Promise)(function (resolve, reject) {
          aFunction(fn);
          var subscription = that.subscribe({
            next: function (value) {
              try {
                return fn(value);
              } catch (e) {
                reject(e);
                subscription.unsubscribe();
              }
            },
            error: reject,
            complete: resolve
          });
        });
      }
    });
    
    redefineAll($Observable, {
      from: function from(x) {
        var C = typeof this === 'function' ? this : $Observable;
        var method = getMethod(anObject(x)[OBSERVABLE]);
        if (method) {
          var observable = anObject(method.call(x));
          return observable.constructor === C ? observable : new C(function (observer) {
            return observable.subscribe(observer);
          });
        }
        return new C(function (observer) {
          var done = false;
          microtask(function () {
            if (!done) {
              try {
                if (forOf(x, false, function (it) {
                  observer.next(it);
                  if (done) return RETURN;
                }) === RETURN) return;
              } catch (e) {
                if (done) throw e;
                observer.error(e);
                return;
              } observer.complete();
            }
          });
          return function () { done = true; };
        });
      },
      of: function of() {
        for (var i = 0, l = arguments.length, items = new Array(l); i < l;) items[i] = arguments[i++];
        return new (typeof this === 'function' ? this : $Observable)(function (observer) {
          var done = false;
          microtask(function () {
            if (!done) {
              for (var j = 0; j < items.length; ++j) {
                observer.next(items[j]);
                if (done) return;
              } observer.complete();
            }
          });
          return function () { done = true; };
        });
      }
    });
    
    hide($Observable.prototype, OBSERVABLE, function () { return this; });
    
    $export($export.G, { Observable: $Observable });
    
    __webpack_require__(/*! ./_set-species */ "./node_modules/core-js/modules/_set-species.js")('Observable');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.promise.finally.js":
    /*!*************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.promise.finally.js ***!
      \*************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    // https://github.com/tc39/proposal-promise-finally
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var core = __webpack_require__(/*! ./_core */ "./node_modules/core-js/modules/_core.js");
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var speciesConstructor = __webpack_require__(/*! ./_species-constructor */ "./node_modules/core-js/modules/_species-constructor.js");
    var promiseResolve = __webpack_require__(/*! ./_promise-resolve */ "./node_modules/core-js/modules/_promise-resolve.js");
    
    $export($export.P + $export.R, 'Promise', { 'finally': function (onFinally) {
      var C = speciesConstructor(this, core.Promise || global.Promise);
      var isFunction = typeof onFinally == 'function';
      return this.then(
        isFunction ? function (x) {
          return promiseResolve(C, onFinally()).then(function () { return x; });
        } : onFinally,
        isFunction ? function (e) {
          return promiseResolve(C, onFinally()).then(function () { throw e; });
        } : onFinally
      );
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.promise.try.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.promise.try.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://github.com/tc39/proposal-promise-try
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var newPromiseCapability = __webpack_require__(/*! ./_new-promise-capability */ "./node_modules/core-js/modules/_new-promise-capability.js");
    var perform = __webpack_require__(/*! ./_perform */ "./node_modules/core-js/modules/_perform.js");
    
    $export($export.S, 'Promise', { 'try': function (callbackfn) {
      var promiseCapability = newPromiseCapability.f(this);
      var result = perform(callbackfn);
      (result.e ? promiseCapability.reject : promiseCapability.resolve)(result.v);
      return promiseCapability.promise;
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.define-metadata.js":
    /*!*********************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.define-metadata.js ***!
      \*********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var toMetaKey = metadata.key;
    var ordinaryDefineOwnMetadata = metadata.set;
    
    metadata.exp({ defineMetadata: function defineMetadata(metadataKey, metadataValue, target, targetKey) {
      ordinaryDefineOwnMetadata(metadataKey, metadataValue, anObject(target), toMetaKey(targetKey));
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.delete-metadata.js":
    /*!*********************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.delete-metadata.js ***!
      \*********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var toMetaKey = metadata.key;
    var getOrCreateMetadataMap = metadata.map;
    var store = metadata.store;
    
    metadata.exp({ deleteMetadata: function deleteMetadata(metadataKey, target /* , targetKey */) {
      var targetKey = arguments.length < 3 ? undefined : toMetaKey(arguments[2]);
      var metadataMap = getOrCreateMetadataMap(anObject(target), targetKey, false);
      if (metadataMap === undefined || !metadataMap['delete'](metadataKey)) return false;
      if (metadataMap.size) return true;
      var targetMetadata = store.get(target);
      targetMetadata['delete'](targetKey);
      return !!targetMetadata.size || store['delete'](target);
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.get-metadata-keys.js":
    /*!***********************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.get-metadata-keys.js ***!
      \***********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var Set = __webpack_require__(/*! ./es6.set */ "./node_modules/core-js/modules/es6.set.js");
    var from = __webpack_require__(/*! ./_array-from-iterable */ "./node_modules/core-js/modules/_array-from-iterable.js");
    var metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var ordinaryOwnMetadataKeys = metadata.keys;
    var toMetaKey = metadata.key;
    
    var ordinaryMetadataKeys = function (O, P) {
      var oKeys = ordinaryOwnMetadataKeys(O, P);
      var parent = getPrototypeOf(O);
      if (parent === null) return oKeys;
      var pKeys = ordinaryMetadataKeys(parent, P);
      return pKeys.length ? oKeys.length ? from(new Set(oKeys.concat(pKeys))) : pKeys : oKeys;
    };
    
    metadata.exp({ getMetadataKeys: function getMetadataKeys(target /* , targetKey */) {
      return ordinaryMetadataKeys(anObject(target), arguments.length < 2 ? undefined : toMetaKey(arguments[1]));
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.get-metadata.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.get-metadata.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var ordinaryHasOwnMetadata = metadata.has;
    var ordinaryGetOwnMetadata = metadata.get;
    var toMetaKey = metadata.key;
    
    var ordinaryGetMetadata = function (MetadataKey, O, P) {
      var hasOwn = ordinaryHasOwnMetadata(MetadataKey, O, P);
      if (hasOwn) return ordinaryGetOwnMetadata(MetadataKey, O, P);
      var parent = getPrototypeOf(O);
      return parent !== null ? ordinaryGetMetadata(MetadataKey, parent, P) : undefined;
    };
    
    metadata.exp({ getMetadata: function getMetadata(metadataKey, target /* , targetKey */) {
      return ordinaryGetMetadata(metadataKey, anObject(target), arguments.length < 3 ? undefined : toMetaKey(arguments[2]));
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.get-own-metadata-keys.js":
    /*!***************************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.get-own-metadata-keys.js ***!
      \***************************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var ordinaryOwnMetadataKeys = metadata.keys;
    var toMetaKey = metadata.key;
    
    metadata.exp({ getOwnMetadataKeys: function getOwnMetadataKeys(target /* , targetKey */) {
      return ordinaryOwnMetadataKeys(anObject(target), arguments.length < 2 ? undefined : toMetaKey(arguments[1]));
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.get-own-metadata.js":
    /*!**********************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.get-own-metadata.js ***!
      \**********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var ordinaryGetOwnMetadata = metadata.get;
    var toMetaKey = metadata.key;
    
    metadata.exp({ getOwnMetadata: function getOwnMetadata(metadataKey, target /* , targetKey */) {
      return ordinaryGetOwnMetadata(metadataKey, anObject(target)
        , arguments.length < 3 ? undefined : toMetaKey(arguments[2]));
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.has-metadata.js":
    /*!******************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.has-metadata.js ***!
      \******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var getPrototypeOf = __webpack_require__(/*! ./_object-gpo */ "./node_modules/core-js/modules/_object-gpo.js");
    var ordinaryHasOwnMetadata = metadata.has;
    var toMetaKey = metadata.key;
    
    var ordinaryHasMetadata = function (MetadataKey, O, P) {
      var hasOwn = ordinaryHasOwnMetadata(MetadataKey, O, P);
      if (hasOwn) return true;
      var parent = getPrototypeOf(O);
      return parent !== null ? ordinaryHasMetadata(MetadataKey, parent, P) : false;
    };
    
    metadata.exp({ hasMetadata: function hasMetadata(metadataKey, target /* , targetKey */) {
      return ordinaryHasMetadata(metadataKey, anObject(target), arguments.length < 3 ? undefined : toMetaKey(arguments[2]));
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.has-own-metadata.js":
    /*!**********************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.has-own-metadata.js ***!
      \**********************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var ordinaryHasOwnMetadata = metadata.has;
    var toMetaKey = metadata.key;
    
    metadata.exp({ hasOwnMetadata: function hasOwnMetadata(metadataKey, target /* , targetKey */) {
      return ordinaryHasOwnMetadata(metadataKey, anObject(target)
        , arguments.length < 3 ? undefined : toMetaKey(arguments[2]));
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.reflect.metadata.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.reflect.metadata.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $metadata = __webpack_require__(/*! ./_metadata */ "./node_modules/core-js/modules/_metadata.js");
    var anObject = __webpack_require__(/*! ./_an-object */ "./node_modules/core-js/modules/_an-object.js");
    var aFunction = __webpack_require__(/*! ./_a-function */ "./node_modules/core-js/modules/_a-function.js");
    var toMetaKey = $metadata.key;
    var ordinaryDefineOwnMetadata = $metadata.set;
    
    $metadata.exp({ metadata: function metadata(metadataKey, metadataValue) {
      return function decorator(target, targetKey) {
        ordinaryDefineOwnMetadata(
          metadataKey, metadataValue,
          (targetKey !== undefined ? anObject : aFunction)(target),
          toMetaKey(targetKey)
        );
      };
    } });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.set.from.js":
    /*!******************************************************!*\
      !*** ./node_modules/core-js/modules/es7.set.from.js ***!
      \******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/proposal-setmap-offrom/#sec-set.from
    __webpack_require__(/*! ./_set-collection-from */ "./node_modules/core-js/modules/_set-collection-from.js")('Set');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.set.of.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/es7.set.of.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/proposal-setmap-offrom/#sec-set.of
    __webpack_require__(/*! ./_set-collection-of */ "./node_modules/core-js/modules/_set-collection-of.js")('Set');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.set.to-json.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.set.to-json.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/DavidBruant/Map-Set.prototype.toJSON
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.P + $export.R, 'Set', { toJSON: __webpack_require__(/*! ./_collection-to-json */ "./node_modules/core-js/modules/_collection-to-json.js")('Set') });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.string.at.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/es7.string.at.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://github.com/mathiasbynens/String.prototype.at
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $at = __webpack_require__(/*! ./_string-at */ "./node_modules/core-js/modules/_string-at.js")(true);
    
    $export($export.P, 'String', {
      at: function at(pos) {
        return $at(this, pos);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.string.match-all.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.string.match-all.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://tc39.github.io/String.prototype.matchAll/
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var defined = __webpack_require__(/*! ./_defined */ "./node_modules/core-js/modules/_defined.js");
    var toLength = __webpack_require__(/*! ./_to-length */ "./node_modules/core-js/modules/_to-length.js");
    var isRegExp = __webpack_require__(/*! ./_is-regexp */ "./node_modules/core-js/modules/_is-regexp.js");
    var getFlags = __webpack_require__(/*! ./_flags */ "./node_modules/core-js/modules/_flags.js");
    var RegExpProto = RegExp.prototype;
    
    var $RegExpStringIterator = function (regexp, string) {
      this._r = regexp;
      this._s = string;
    };
    
    __webpack_require__(/*! ./_iter-create */ "./node_modules/core-js/modules/_iter-create.js")($RegExpStringIterator, 'RegExp String', function next() {
      var match = this._r.exec(this._s);
      return { value: match, done: match === null };
    });
    
    $export($export.P, 'String', {
      matchAll: function matchAll(regexp) {
        defined(this);
        if (!isRegExp(regexp)) throw TypeError(regexp + ' is not a regexp!');
        var S = String(this);
        var flags = 'flags' in RegExpProto ? String(regexp.flags) : getFlags.call(regexp);
        var rx = new RegExp(regexp.source, ~flags.indexOf('g') ? flags : 'g' + flags);
        rx.lastIndex = toLength(regexp.lastIndex);
        return new $RegExpStringIterator(rx, S);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.string.pad-end.js":
    /*!************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.string.pad-end.js ***!
      \************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://github.com/tc39/proposal-string-pad-start-end
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $pad = __webpack_require__(/*! ./_string-pad */ "./node_modules/core-js/modules/_string-pad.js");
    var userAgent = __webpack_require__(/*! ./_user-agent */ "./node_modules/core-js/modules/_user-agent.js");
    
    // https://github.com/zloirock/core-js/issues/280
    $export($export.P + $export.F * /Version\/10\.\d+(\.\d+)? Safari\//.test(userAgent), 'String', {
      padEnd: function padEnd(maxLength /* , fillString = ' ' */) {
        return $pad(this, maxLength, arguments.length > 1 ? arguments[1] : undefined, false);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.string.pad-start.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.string.pad-start.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://github.com/tc39/proposal-string-pad-start-end
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $pad = __webpack_require__(/*! ./_string-pad */ "./node_modules/core-js/modules/_string-pad.js");
    var userAgent = __webpack_require__(/*! ./_user-agent */ "./node_modules/core-js/modules/_user-agent.js");
    
    // https://github.com/zloirock/core-js/issues/280
    $export($export.P + $export.F * /Version\/10\.\d+(\.\d+)? Safari\//.test(userAgent), 'String', {
      padStart: function padStart(maxLength /* , fillString = ' ' */) {
        return $pad(this, maxLength, arguments.length > 1 ? arguments[1] : undefined, true);
      }
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.string.trim-left.js":
    /*!**************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.string.trim-left.js ***!
      \**************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://github.com/sebmarkbage/ecmascript-string-left-right-trim
    __webpack_require__(/*! ./_string-trim */ "./node_modules/core-js/modules/_string-trim.js")('trimLeft', function ($trim) {
      return function trimLeft() {
        return $trim(this, 1);
      };
    }, 'trimStart');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.string.trim-right.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.string.trim-right.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    // https://github.com/sebmarkbage/ecmascript-string-left-right-trim
    __webpack_require__(/*! ./_string-trim */ "./node_modules/core-js/modules/_string-trim.js")('trimRight', function ($trim) {
      return function trimRight() {
        return $trim(this, 2);
      };
    }, 'trimEnd');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.symbol.async-iterator.js":
    /*!*******************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.symbol.async-iterator.js ***!
      \*******************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_wks-define */ "./node_modules/core-js/modules/_wks-define.js")('asyncIterator');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.symbol.observable.js":
    /*!***************************************************************!*\
      !*** ./node_modules/core-js/modules/es7.symbol.observable.js ***!
      \***************************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./_wks-define */ "./node_modules/core-js/modules/_wks-define.js")('observable');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.system.global.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.system.global.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://github.com/tc39/proposal-global
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    
    $export($export.S, 'System', { global: __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js") });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.weak-map.from.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.weak-map.from.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/proposal-setmap-offrom/#sec-weakmap.from
    __webpack_require__(/*! ./_set-collection-from */ "./node_modules/core-js/modules/_set-collection-from.js")('WeakMap');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.weak-map.of.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.weak-map.of.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/proposal-setmap-offrom/#sec-weakmap.of
    __webpack_require__(/*! ./_set-collection-of */ "./node_modules/core-js/modules/_set-collection-of.js")('WeakMap');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.weak-set.from.js":
    /*!***********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.weak-set.from.js ***!
      \***********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/proposal-setmap-offrom/#sec-weakset.from
    __webpack_require__(/*! ./_set-collection-from */ "./node_modules/core-js/modules/_set-collection-from.js")('WeakSet');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/es7.weak-set.of.js":
    /*!*********************************************************!*\
      !*** ./node_modules/core-js/modules/es7.weak-set.of.js ***!
      \*********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // https://tc39.github.io/proposal-setmap-offrom/#sec-weakset.of
    __webpack_require__(/*! ./_set-collection-of */ "./node_modules/core-js/modules/_set-collection-of.js")('WeakSet');
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/web.dom.iterable.js":
    /*!**********************************************************!*\
      !*** ./node_modules/core-js/modules/web.dom.iterable.js ***!
      \**********************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $iterators = __webpack_require__(/*! ./es6.array.iterator */ "./node_modules/core-js/modules/es6.array.iterator.js");
    var getKeys = __webpack_require__(/*! ./_object-keys */ "./node_modules/core-js/modules/_object-keys.js");
    var redefine = __webpack_require__(/*! ./_redefine */ "./node_modules/core-js/modules/_redefine.js");
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var hide = __webpack_require__(/*! ./_hide */ "./node_modules/core-js/modules/_hide.js");
    var Iterators = __webpack_require__(/*! ./_iterators */ "./node_modules/core-js/modules/_iterators.js");
    var wks = __webpack_require__(/*! ./_wks */ "./node_modules/core-js/modules/_wks.js");
    var ITERATOR = wks('iterator');
    var TO_STRING_TAG = wks('toStringTag');
    var ArrayValues = Iterators.Array;
    
    var DOMIterables = {
      CSSRuleList: true, // TODO: Not spec compliant, should be false.
      CSSStyleDeclaration: false,
      CSSValueList: false,
      ClientRectList: false,
      DOMRectList: false,
      DOMStringList: false,
      DOMTokenList: true,
      DataTransferItemList: false,
      FileList: false,
      HTMLAllCollection: false,
      HTMLCollection: false,
      HTMLFormElement: false,
      HTMLSelectElement: false,
      MediaList: true, // TODO: Not spec compliant, should be false.
      MimeTypeArray: false,
      NamedNodeMap: false,
      NodeList: true,
      PaintRequestList: false,
      Plugin: false,
      PluginArray: false,
      SVGLengthList: false,
      SVGNumberList: false,
      SVGPathSegList: false,
      SVGPointList: false,
      SVGStringList: false,
      SVGTransformList: false,
      SourceBufferList: false,
      StyleSheetList: true, // TODO: Not spec compliant, should be false.
      TextTrackCueList: false,
      TextTrackList: false,
      TouchList: false
    };
    
    for (var collections = getKeys(DOMIterables), i = 0; i < collections.length; i++) {
      var NAME = collections[i];
      var explicit = DOMIterables[NAME];
      var Collection = global[NAME];
      var proto = Collection && Collection.prototype;
      var key;
      if (proto) {
        if (!proto[ITERATOR]) hide(proto, ITERATOR, ArrayValues);
        if (!proto[TO_STRING_TAG]) hide(proto, TO_STRING_TAG, NAME);
        Iterators[NAME] = ArrayValues;
        if (explicit) for (key in $iterators) if (!proto[key]) redefine(proto, key, $iterators[key], true);
      }
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/web.immediate.js":
    /*!*******************************************************!*\
      !*** ./node_modules/core-js/modules/web.immediate.js ***!
      \*******************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var $task = __webpack_require__(/*! ./_task */ "./node_modules/core-js/modules/_task.js");
    $export($export.G + $export.B, {
      setImmediate: $task.set,
      clearImmediate: $task.clear
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/modules/web.timers.js":
    /*!****************************************************!*\
      !*** ./node_modules/core-js/modules/web.timers.js ***!
      \****************************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    // ie9- setTimeout & setInterval additional parameters fix
    var global = __webpack_require__(/*! ./_global */ "./node_modules/core-js/modules/_global.js");
    var $export = __webpack_require__(/*! ./_export */ "./node_modules/core-js/modules/_export.js");
    var userAgent = __webpack_require__(/*! ./_user-agent */ "./node_modules/core-js/modules/_user-agent.js");
    var slice = [].slice;
    var MSIE = /MSIE .\./.test(userAgent); // <- dirty ie9- check
    var wrap = function (set) {
      return function (fn, time /* , ...args */) {
        var boundArgs = arguments.length > 2;
        var args = boundArgs ? slice.call(arguments, 2) : false;
        return set(boundArgs ? function () {
          // eslint-disable-next-line no-new-func
          (typeof fn == 'function' ? fn : Function(fn)).apply(this, args);
        } : fn, time);
      };
    };
    $export($export.G + $export.B + $export.F * MSIE, {
      setTimeout: wrap(global.setTimeout),
      setInterval: wrap(global.setInterval)
    });
    
    
    /***/ }),
    
    /***/ "./node_modules/core-js/shim.js":
    /*!**************************************!*\
      !*** ./node_modules/core-js/shim.js ***!
      \**************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! ./modules/es6.symbol */ "./node_modules/core-js/modules/es6.symbol.js");
    __webpack_require__(/*! ./modules/es6.object.create */ "./node_modules/core-js/modules/es6.object.create.js");
    __webpack_require__(/*! ./modules/es6.object.define-property */ "./node_modules/core-js/modules/es6.object.define-property.js");
    __webpack_require__(/*! ./modules/es6.object.define-properties */ "./node_modules/core-js/modules/es6.object.define-properties.js");
    __webpack_require__(/*! ./modules/es6.object.get-own-property-descriptor */ "./node_modules/core-js/modules/es6.object.get-own-property-descriptor.js");
    __webpack_require__(/*! ./modules/es6.object.get-prototype-of */ "./node_modules/core-js/modules/es6.object.get-prototype-of.js");
    __webpack_require__(/*! ./modules/es6.object.keys */ "./node_modules/core-js/modules/es6.object.keys.js");
    __webpack_require__(/*! ./modules/es6.object.get-own-property-names */ "./node_modules/core-js/modules/es6.object.get-own-property-names.js");
    __webpack_require__(/*! ./modules/es6.object.freeze */ "./node_modules/core-js/modules/es6.object.freeze.js");
    __webpack_require__(/*! ./modules/es6.object.seal */ "./node_modules/core-js/modules/es6.object.seal.js");
    __webpack_require__(/*! ./modules/es6.object.prevent-extensions */ "./node_modules/core-js/modules/es6.object.prevent-extensions.js");
    __webpack_require__(/*! ./modules/es6.object.is-frozen */ "./node_modules/core-js/modules/es6.object.is-frozen.js");
    __webpack_require__(/*! ./modules/es6.object.is-sealed */ "./node_modules/core-js/modules/es6.object.is-sealed.js");
    __webpack_require__(/*! ./modules/es6.object.is-extensible */ "./node_modules/core-js/modules/es6.object.is-extensible.js");
    __webpack_require__(/*! ./modules/es6.object.assign */ "./node_modules/core-js/modules/es6.object.assign.js");
    __webpack_require__(/*! ./modules/es6.object.is */ "./node_modules/core-js/modules/es6.object.is.js");
    __webpack_require__(/*! ./modules/es6.object.set-prototype-of */ "./node_modules/core-js/modules/es6.object.set-prototype-of.js");
    __webpack_require__(/*! ./modules/es6.object.to-string */ "./node_modules/core-js/modules/es6.object.to-string.js");
    __webpack_require__(/*! ./modules/es6.function.bind */ "./node_modules/core-js/modules/es6.function.bind.js");
    __webpack_require__(/*! ./modules/es6.function.name */ "./node_modules/core-js/modules/es6.function.name.js");
    __webpack_require__(/*! ./modules/es6.function.has-instance */ "./node_modules/core-js/modules/es6.function.has-instance.js");
    __webpack_require__(/*! ./modules/es6.parse-int */ "./node_modules/core-js/modules/es6.parse-int.js");
    __webpack_require__(/*! ./modules/es6.parse-float */ "./node_modules/core-js/modules/es6.parse-float.js");
    __webpack_require__(/*! ./modules/es6.number.constructor */ "./node_modules/core-js/modules/es6.number.constructor.js");
    __webpack_require__(/*! ./modules/es6.number.to-fixed */ "./node_modules/core-js/modules/es6.number.to-fixed.js");
    __webpack_require__(/*! ./modules/es6.number.to-precision */ "./node_modules/core-js/modules/es6.number.to-precision.js");
    __webpack_require__(/*! ./modules/es6.number.epsilon */ "./node_modules/core-js/modules/es6.number.epsilon.js");
    __webpack_require__(/*! ./modules/es6.number.is-finite */ "./node_modules/core-js/modules/es6.number.is-finite.js");
    __webpack_require__(/*! ./modules/es6.number.is-integer */ "./node_modules/core-js/modules/es6.number.is-integer.js");
    __webpack_require__(/*! ./modules/es6.number.is-nan */ "./node_modules/core-js/modules/es6.number.is-nan.js");
    __webpack_require__(/*! ./modules/es6.number.is-safe-integer */ "./node_modules/core-js/modules/es6.number.is-safe-integer.js");
    __webpack_require__(/*! ./modules/es6.number.max-safe-integer */ "./node_modules/core-js/modules/es6.number.max-safe-integer.js");
    __webpack_require__(/*! ./modules/es6.number.min-safe-integer */ "./node_modules/core-js/modules/es6.number.min-safe-integer.js");
    __webpack_require__(/*! ./modules/es6.number.parse-float */ "./node_modules/core-js/modules/es6.number.parse-float.js");
    __webpack_require__(/*! ./modules/es6.number.parse-int */ "./node_modules/core-js/modules/es6.number.parse-int.js");
    __webpack_require__(/*! ./modules/es6.math.acosh */ "./node_modules/core-js/modules/es6.math.acosh.js");
    __webpack_require__(/*! ./modules/es6.math.asinh */ "./node_modules/core-js/modules/es6.math.asinh.js");
    __webpack_require__(/*! ./modules/es6.math.atanh */ "./node_modules/core-js/modules/es6.math.atanh.js");
    __webpack_require__(/*! ./modules/es6.math.cbrt */ "./node_modules/core-js/modules/es6.math.cbrt.js");
    __webpack_require__(/*! ./modules/es6.math.clz32 */ "./node_modules/core-js/modules/es6.math.clz32.js");
    __webpack_require__(/*! ./modules/es6.math.cosh */ "./node_modules/core-js/modules/es6.math.cosh.js");
    __webpack_require__(/*! ./modules/es6.math.expm1 */ "./node_modules/core-js/modules/es6.math.expm1.js");
    __webpack_require__(/*! ./modules/es6.math.fround */ "./node_modules/core-js/modules/es6.math.fround.js");
    __webpack_require__(/*! ./modules/es6.math.hypot */ "./node_modules/core-js/modules/es6.math.hypot.js");
    __webpack_require__(/*! ./modules/es6.math.imul */ "./node_modules/core-js/modules/es6.math.imul.js");
    __webpack_require__(/*! ./modules/es6.math.log10 */ "./node_modules/core-js/modules/es6.math.log10.js");
    __webpack_require__(/*! ./modules/es6.math.log1p */ "./node_modules/core-js/modules/es6.math.log1p.js");
    __webpack_require__(/*! ./modules/es6.math.log2 */ "./node_modules/core-js/modules/es6.math.log2.js");
    __webpack_require__(/*! ./modules/es6.math.sign */ "./node_modules/core-js/modules/es6.math.sign.js");
    __webpack_require__(/*! ./modules/es6.math.sinh */ "./node_modules/core-js/modules/es6.math.sinh.js");
    __webpack_require__(/*! ./modules/es6.math.tanh */ "./node_modules/core-js/modules/es6.math.tanh.js");
    __webpack_require__(/*! ./modules/es6.math.trunc */ "./node_modules/core-js/modules/es6.math.trunc.js");
    __webpack_require__(/*! ./modules/es6.string.from-code-point */ "./node_modules/core-js/modules/es6.string.from-code-point.js");
    __webpack_require__(/*! ./modules/es6.string.raw */ "./node_modules/core-js/modules/es6.string.raw.js");
    __webpack_require__(/*! ./modules/es6.string.trim */ "./node_modules/core-js/modules/es6.string.trim.js");
    __webpack_require__(/*! ./modules/es6.string.iterator */ "./node_modules/core-js/modules/es6.string.iterator.js");
    __webpack_require__(/*! ./modules/es6.string.code-point-at */ "./node_modules/core-js/modules/es6.string.code-point-at.js");
    __webpack_require__(/*! ./modules/es6.string.ends-with */ "./node_modules/core-js/modules/es6.string.ends-with.js");
    __webpack_require__(/*! ./modules/es6.string.includes */ "./node_modules/core-js/modules/es6.string.includes.js");
    __webpack_require__(/*! ./modules/es6.string.repeat */ "./node_modules/core-js/modules/es6.string.repeat.js");
    __webpack_require__(/*! ./modules/es6.string.starts-with */ "./node_modules/core-js/modules/es6.string.starts-with.js");
    __webpack_require__(/*! ./modules/es6.string.anchor */ "./node_modules/core-js/modules/es6.string.anchor.js");
    __webpack_require__(/*! ./modules/es6.string.big */ "./node_modules/core-js/modules/es6.string.big.js");
    __webpack_require__(/*! ./modules/es6.string.blink */ "./node_modules/core-js/modules/es6.string.blink.js");
    __webpack_require__(/*! ./modules/es6.string.bold */ "./node_modules/core-js/modules/es6.string.bold.js");
    __webpack_require__(/*! ./modules/es6.string.fixed */ "./node_modules/core-js/modules/es6.string.fixed.js");
    __webpack_require__(/*! ./modules/es6.string.fontcolor */ "./node_modules/core-js/modules/es6.string.fontcolor.js");
    __webpack_require__(/*! ./modules/es6.string.fontsize */ "./node_modules/core-js/modules/es6.string.fontsize.js");
    __webpack_require__(/*! ./modules/es6.string.italics */ "./node_modules/core-js/modules/es6.string.italics.js");
    __webpack_require__(/*! ./modules/es6.string.link */ "./node_modules/core-js/modules/es6.string.link.js");
    __webpack_require__(/*! ./modules/es6.string.small */ "./node_modules/core-js/modules/es6.string.small.js");
    __webpack_require__(/*! ./modules/es6.string.strike */ "./node_modules/core-js/modules/es6.string.strike.js");
    __webpack_require__(/*! ./modules/es6.string.sub */ "./node_modules/core-js/modules/es6.string.sub.js");
    __webpack_require__(/*! ./modules/es6.string.sup */ "./node_modules/core-js/modules/es6.string.sup.js");
    __webpack_require__(/*! ./modules/es6.date.now */ "./node_modules/core-js/modules/es6.date.now.js");
    __webpack_require__(/*! ./modules/es6.date.to-json */ "./node_modules/core-js/modules/es6.date.to-json.js");
    __webpack_require__(/*! ./modules/es6.date.to-iso-string */ "./node_modules/core-js/modules/es6.date.to-iso-string.js");
    __webpack_require__(/*! ./modules/es6.date.to-string */ "./node_modules/core-js/modules/es6.date.to-string.js");
    __webpack_require__(/*! ./modules/es6.date.to-primitive */ "./node_modules/core-js/modules/es6.date.to-primitive.js");
    __webpack_require__(/*! ./modules/es6.array.is-array */ "./node_modules/core-js/modules/es6.array.is-array.js");
    __webpack_require__(/*! ./modules/es6.array.from */ "./node_modules/core-js/modules/es6.array.from.js");
    __webpack_require__(/*! ./modules/es6.array.of */ "./node_modules/core-js/modules/es6.array.of.js");
    __webpack_require__(/*! ./modules/es6.array.join */ "./node_modules/core-js/modules/es6.array.join.js");
    __webpack_require__(/*! ./modules/es6.array.slice */ "./node_modules/core-js/modules/es6.array.slice.js");
    __webpack_require__(/*! ./modules/es6.array.sort */ "./node_modules/core-js/modules/es6.array.sort.js");
    __webpack_require__(/*! ./modules/es6.array.for-each */ "./node_modules/core-js/modules/es6.array.for-each.js");
    __webpack_require__(/*! ./modules/es6.array.map */ "./node_modules/core-js/modules/es6.array.map.js");
    __webpack_require__(/*! ./modules/es6.array.filter */ "./node_modules/core-js/modules/es6.array.filter.js");
    __webpack_require__(/*! ./modules/es6.array.some */ "./node_modules/core-js/modules/es6.array.some.js");
    __webpack_require__(/*! ./modules/es6.array.every */ "./node_modules/core-js/modules/es6.array.every.js");
    __webpack_require__(/*! ./modules/es6.array.reduce */ "./node_modules/core-js/modules/es6.array.reduce.js");
    __webpack_require__(/*! ./modules/es6.array.reduce-right */ "./node_modules/core-js/modules/es6.array.reduce-right.js");
    __webpack_require__(/*! ./modules/es6.array.index-of */ "./node_modules/core-js/modules/es6.array.index-of.js");
    __webpack_require__(/*! ./modules/es6.array.last-index-of */ "./node_modules/core-js/modules/es6.array.last-index-of.js");
    __webpack_require__(/*! ./modules/es6.array.copy-within */ "./node_modules/core-js/modules/es6.array.copy-within.js");
    __webpack_require__(/*! ./modules/es6.array.fill */ "./node_modules/core-js/modules/es6.array.fill.js");
    __webpack_require__(/*! ./modules/es6.array.find */ "./node_modules/core-js/modules/es6.array.find.js");
    __webpack_require__(/*! ./modules/es6.array.find-index */ "./node_modules/core-js/modules/es6.array.find-index.js");
    __webpack_require__(/*! ./modules/es6.array.species */ "./node_modules/core-js/modules/es6.array.species.js");
    __webpack_require__(/*! ./modules/es6.array.iterator */ "./node_modules/core-js/modules/es6.array.iterator.js");
    __webpack_require__(/*! ./modules/es6.regexp.constructor */ "./node_modules/core-js/modules/es6.regexp.constructor.js");
    __webpack_require__(/*! ./modules/es6.regexp.exec */ "./node_modules/core-js/modules/es6.regexp.exec.js");
    __webpack_require__(/*! ./modules/es6.regexp.to-string */ "./node_modules/core-js/modules/es6.regexp.to-string.js");
    __webpack_require__(/*! ./modules/es6.regexp.flags */ "./node_modules/core-js/modules/es6.regexp.flags.js");
    __webpack_require__(/*! ./modules/es6.regexp.match */ "./node_modules/core-js/modules/es6.regexp.match.js");
    __webpack_require__(/*! ./modules/es6.regexp.replace */ "./node_modules/core-js/modules/es6.regexp.replace.js");
    __webpack_require__(/*! ./modules/es6.regexp.search */ "./node_modules/core-js/modules/es6.regexp.search.js");
    __webpack_require__(/*! ./modules/es6.regexp.split */ "./node_modules/core-js/modules/es6.regexp.split.js");
    __webpack_require__(/*! ./modules/es6.promise */ "./node_modules/core-js/modules/es6.promise.js");
    __webpack_require__(/*! ./modules/es6.map */ "./node_modules/core-js/modules/es6.map.js");
    __webpack_require__(/*! ./modules/es6.set */ "./node_modules/core-js/modules/es6.set.js");
    __webpack_require__(/*! ./modules/es6.weak-map */ "./node_modules/core-js/modules/es6.weak-map.js");
    __webpack_require__(/*! ./modules/es6.weak-set */ "./node_modules/core-js/modules/es6.weak-set.js");
    __webpack_require__(/*! ./modules/es6.typed.array-buffer */ "./node_modules/core-js/modules/es6.typed.array-buffer.js");
    __webpack_require__(/*! ./modules/es6.typed.data-view */ "./node_modules/core-js/modules/es6.typed.data-view.js");
    __webpack_require__(/*! ./modules/es6.typed.int8-array */ "./node_modules/core-js/modules/es6.typed.int8-array.js");
    __webpack_require__(/*! ./modules/es6.typed.uint8-array */ "./node_modules/core-js/modules/es6.typed.uint8-array.js");
    __webpack_require__(/*! ./modules/es6.typed.uint8-clamped-array */ "./node_modules/core-js/modules/es6.typed.uint8-clamped-array.js");
    __webpack_require__(/*! ./modules/es6.typed.int16-array */ "./node_modules/core-js/modules/es6.typed.int16-array.js");
    __webpack_require__(/*! ./modules/es6.typed.uint16-array */ "./node_modules/core-js/modules/es6.typed.uint16-array.js");
    __webpack_require__(/*! ./modules/es6.typed.int32-array */ "./node_modules/core-js/modules/es6.typed.int32-array.js");
    __webpack_require__(/*! ./modules/es6.typed.uint32-array */ "./node_modules/core-js/modules/es6.typed.uint32-array.js");
    __webpack_require__(/*! ./modules/es6.typed.float32-array */ "./node_modules/core-js/modules/es6.typed.float32-array.js");
    __webpack_require__(/*! ./modules/es6.typed.float64-array */ "./node_modules/core-js/modules/es6.typed.float64-array.js");
    __webpack_require__(/*! ./modules/es6.reflect.apply */ "./node_modules/core-js/modules/es6.reflect.apply.js");
    __webpack_require__(/*! ./modules/es6.reflect.construct */ "./node_modules/core-js/modules/es6.reflect.construct.js");
    __webpack_require__(/*! ./modules/es6.reflect.define-property */ "./node_modules/core-js/modules/es6.reflect.define-property.js");
    __webpack_require__(/*! ./modules/es6.reflect.delete-property */ "./node_modules/core-js/modules/es6.reflect.delete-property.js");
    __webpack_require__(/*! ./modules/es6.reflect.enumerate */ "./node_modules/core-js/modules/es6.reflect.enumerate.js");
    __webpack_require__(/*! ./modules/es6.reflect.get */ "./node_modules/core-js/modules/es6.reflect.get.js");
    __webpack_require__(/*! ./modules/es6.reflect.get-own-property-descriptor */ "./node_modules/core-js/modules/es6.reflect.get-own-property-descriptor.js");
    __webpack_require__(/*! ./modules/es6.reflect.get-prototype-of */ "./node_modules/core-js/modules/es6.reflect.get-prototype-of.js");
    __webpack_require__(/*! ./modules/es6.reflect.has */ "./node_modules/core-js/modules/es6.reflect.has.js");
    __webpack_require__(/*! ./modules/es6.reflect.is-extensible */ "./node_modules/core-js/modules/es6.reflect.is-extensible.js");
    __webpack_require__(/*! ./modules/es6.reflect.own-keys */ "./node_modules/core-js/modules/es6.reflect.own-keys.js");
    __webpack_require__(/*! ./modules/es6.reflect.prevent-extensions */ "./node_modules/core-js/modules/es6.reflect.prevent-extensions.js");
    __webpack_require__(/*! ./modules/es6.reflect.set */ "./node_modules/core-js/modules/es6.reflect.set.js");
    __webpack_require__(/*! ./modules/es6.reflect.set-prototype-of */ "./node_modules/core-js/modules/es6.reflect.set-prototype-of.js");
    __webpack_require__(/*! ./modules/es7.array.includes */ "./node_modules/core-js/modules/es7.array.includes.js");
    __webpack_require__(/*! ./modules/es7.array.flat-map */ "./node_modules/core-js/modules/es7.array.flat-map.js");
    __webpack_require__(/*! ./modules/es7.array.flatten */ "./node_modules/core-js/modules/es7.array.flatten.js");
    __webpack_require__(/*! ./modules/es7.string.at */ "./node_modules/core-js/modules/es7.string.at.js");
    __webpack_require__(/*! ./modules/es7.string.pad-start */ "./node_modules/core-js/modules/es7.string.pad-start.js");
    __webpack_require__(/*! ./modules/es7.string.pad-end */ "./node_modules/core-js/modules/es7.string.pad-end.js");
    __webpack_require__(/*! ./modules/es7.string.trim-left */ "./node_modules/core-js/modules/es7.string.trim-left.js");
    __webpack_require__(/*! ./modules/es7.string.trim-right */ "./node_modules/core-js/modules/es7.string.trim-right.js");
    __webpack_require__(/*! ./modules/es7.string.match-all */ "./node_modules/core-js/modules/es7.string.match-all.js");
    __webpack_require__(/*! ./modules/es7.symbol.async-iterator */ "./node_modules/core-js/modules/es7.symbol.async-iterator.js");
    __webpack_require__(/*! ./modules/es7.symbol.observable */ "./node_modules/core-js/modules/es7.symbol.observable.js");
    __webpack_require__(/*! ./modules/es7.object.get-own-property-descriptors */ "./node_modules/core-js/modules/es7.object.get-own-property-descriptors.js");
    __webpack_require__(/*! ./modules/es7.object.values */ "./node_modules/core-js/modules/es7.object.values.js");
    __webpack_require__(/*! ./modules/es7.object.entries */ "./node_modules/core-js/modules/es7.object.entries.js");
    __webpack_require__(/*! ./modules/es7.object.define-getter */ "./node_modules/core-js/modules/es7.object.define-getter.js");
    __webpack_require__(/*! ./modules/es7.object.define-setter */ "./node_modules/core-js/modules/es7.object.define-setter.js");
    __webpack_require__(/*! ./modules/es7.object.lookup-getter */ "./node_modules/core-js/modules/es7.object.lookup-getter.js");
    __webpack_require__(/*! ./modules/es7.object.lookup-setter */ "./node_modules/core-js/modules/es7.object.lookup-setter.js");
    __webpack_require__(/*! ./modules/es7.map.to-json */ "./node_modules/core-js/modules/es7.map.to-json.js");
    __webpack_require__(/*! ./modules/es7.set.to-json */ "./node_modules/core-js/modules/es7.set.to-json.js");
    __webpack_require__(/*! ./modules/es7.map.of */ "./node_modules/core-js/modules/es7.map.of.js");
    __webpack_require__(/*! ./modules/es7.set.of */ "./node_modules/core-js/modules/es7.set.of.js");
    __webpack_require__(/*! ./modules/es7.weak-map.of */ "./node_modules/core-js/modules/es7.weak-map.of.js");
    __webpack_require__(/*! ./modules/es7.weak-set.of */ "./node_modules/core-js/modules/es7.weak-set.of.js");
    __webpack_require__(/*! ./modules/es7.map.from */ "./node_modules/core-js/modules/es7.map.from.js");
    __webpack_require__(/*! ./modules/es7.set.from */ "./node_modules/core-js/modules/es7.set.from.js");
    __webpack_require__(/*! ./modules/es7.weak-map.from */ "./node_modules/core-js/modules/es7.weak-map.from.js");
    __webpack_require__(/*! ./modules/es7.weak-set.from */ "./node_modules/core-js/modules/es7.weak-set.from.js");
    __webpack_require__(/*! ./modules/es7.global */ "./node_modules/core-js/modules/es7.global.js");
    __webpack_require__(/*! ./modules/es7.system.global */ "./node_modules/core-js/modules/es7.system.global.js");
    __webpack_require__(/*! ./modules/es7.error.is-error */ "./node_modules/core-js/modules/es7.error.is-error.js");
    __webpack_require__(/*! ./modules/es7.math.clamp */ "./node_modules/core-js/modules/es7.math.clamp.js");
    __webpack_require__(/*! ./modules/es7.math.deg-per-rad */ "./node_modules/core-js/modules/es7.math.deg-per-rad.js");
    __webpack_require__(/*! ./modules/es7.math.degrees */ "./node_modules/core-js/modules/es7.math.degrees.js");
    __webpack_require__(/*! ./modules/es7.math.fscale */ "./node_modules/core-js/modules/es7.math.fscale.js");
    __webpack_require__(/*! ./modules/es7.math.iaddh */ "./node_modules/core-js/modules/es7.math.iaddh.js");
    __webpack_require__(/*! ./modules/es7.math.isubh */ "./node_modules/core-js/modules/es7.math.isubh.js");
    __webpack_require__(/*! ./modules/es7.math.imulh */ "./node_modules/core-js/modules/es7.math.imulh.js");
    __webpack_require__(/*! ./modules/es7.math.rad-per-deg */ "./node_modules/core-js/modules/es7.math.rad-per-deg.js");
    __webpack_require__(/*! ./modules/es7.math.radians */ "./node_modules/core-js/modules/es7.math.radians.js");
    __webpack_require__(/*! ./modules/es7.math.scale */ "./node_modules/core-js/modules/es7.math.scale.js");
    __webpack_require__(/*! ./modules/es7.math.umulh */ "./node_modules/core-js/modules/es7.math.umulh.js");
    __webpack_require__(/*! ./modules/es7.math.signbit */ "./node_modules/core-js/modules/es7.math.signbit.js");
    __webpack_require__(/*! ./modules/es7.promise.finally */ "./node_modules/core-js/modules/es7.promise.finally.js");
    __webpack_require__(/*! ./modules/es7.promise.try */ "./node_modules/core-js/modules/es7.promise.try.js");
    __webpack_require__(/*! ./modules/es7.reflect.define-metadata */ "./node_modules/core-js/modules/es7.reflect.define-metadata.js");
    __webpack_require__(/*! ./modules/es7.reflect.delete-metadata */ "./node_modules/core-js/modules/es7.reflect.delete-metadata.js");
    __webpack_require__(/*! ./modules/es7.reflect.get-metadata */ "./node_modules/core-js/modules/es7.reflect.get-metadata.js");
    __webpack_require__(/*! ./modules/es7.reflect.get-metadata-keys */ "./node_modules/core-js/modules/es7.reflect.get-metadata-keys.js");
    __webpack_require__(/*! ./modules/es7.reflect.get-own-metadata */ "./node_modules/core-js/modules/es7.reflect.get-own-metadata.js");
    __webpack_require__(/*! ./modules/es7.reflect.get-own-metadata-keys */ "./node_modules/core-js/modules/es7.reflect.get-own-metadata-keys.js");
    __webpack_require__(/*! ./modules/es7.reflect.has-metadata */ "./node_modules/core-js/modules/es7.reflect.has-metadata.js");
    __webpack_require__(/*! ./modules/es7.reflect.has-own-metadata */ "./node_modules/core-js/modules/es7.reflect.has-own-metadata.js");
    __webpack_require__(/*! ./modules/es7.reflect.metadata */ "./node_modules/core-js/modules/es7.reflect.metadata.js");
    __webpack_require__(/*! ./modules/es7.asap */ "./node_modules/core-js/modules/es7.asap.js");
    __webpack_require__(/*! ./modules/es7.observable */ "./node_modules/core-js/modules/es7.observable.js");
    __webpack_require__(/*! ./modules/web.timers */ "./node_modules/core-js/modules/web.timers.js");
    __webpack_require__(/*! ./modules/web.immediate */ "./node_modules/core-js/modules/web.immediate.js");
    __webpack_require__(/*! ./modules/web.dom.iterable */ "./node_modules/core-js/modules/web.dom.iterable.js");
    module.exports = __webpack_require__(/*! ./modules/_core */ "./node_modules/core-js/modules/_core.js");
    
    
    /***/ }),
    
    /***/ "./node_modules/ieee754/index.js":
    /*!***************************************!*\
      !*** ./node_modules/ieee754/index.js ***!
      \***************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    exports.read = function (buffer, offset, isLE, mLen, nBytes) {
      var e, m
      var eLen = (nBytes * 8) - mLen - 1
      var eMax = (1 << eLen) - 1
      var eBias = eMax >> 1
      var nBits = -7
      var i = isLE ? (nBytes - 1) : 0
      var d = isLE ? -1 : 1
      var s = buffer[offset + i]
    
      i += d
    
      e = s & ((1 << (-nBits)) - 1)
      s >>= (-nBits)
      nBits += eLen
      for (; nBits > 0; e = (e * 256) + buffer[offset + i], i += d, nBits -= 8) {}
    
      m = e & ((1 << (-nBits)) - 1)
      e >>= (-nBits)
      nBits += mLen
      for (; nBits > 0; m = (m * 256) + buffer[offset + i], i += d, nBits -= 8) {}
    
      if (e === 0) {
        e = 1 - eBias
      } else if (e === eMax) {
        return m ? NaN : ((s ? -1 : 1) * Infinity)
      } else {
        m = m + Math.pow(2, mLen)
        e = e - eBias
      }
      return (s ? -1 : 1) * m * Math.pow(2, e - mLen)
    }
    
    exports.write = function (buffer, value, offset, isLE, mLen, nBytes) {
      var e, m, c
      var eLen = (nBytes * 8) - mLen - 1
      var eMax = (1 << eLen) - 1
      var eBias = eMax >> 1
      var rt = (mLen === 23 ? Math.pow(2, -24) - Math.pow(2, -77) : 0)
      var i = isLE ? 0 : (nBytes - 1)
      var d = isLE ? 1 : -1
      var s = value < 0 || (value === 0 && 1 / value < 0) ? 1 : 0
    
      value = Math.abs(value)
    
      if (isNaN(value) || value === Infinity) {
        m = isNaN(value) ? 1 : 0
        e = eMax
      } else {
        e = Math.floor(Math.log(value) / Math.LN2)
        if (value * (c = Math.pow(2, -e)) < 1) {
          e--
          c *= 2
        }
        if (e + eBias >= 1) {
          value += rt / c
        } else {
          value += rt * Math.pow(2, 1 - eBias)
        }
        if (value * c >= 2) {
          e++
          c /= 2
        }
    
        if (e + eBias >= eMax) {
          m = 0
          e = eMax
        } else if (e + eBias >= 1) {
          m = ((value * c) - 1) * Math.pow(2, mLen)
          e = e + eBias
        } else {
          m = value * Math.pow(2, eBias - 1) * Math.pow(2, mLen)
          e = 0
        }
      }
    
      for (; mLen >= 8; buffer[offset + i] = m & 0xff, i += d, m /= 256, mLen -= 8) {}
    
      e = (e << mLen) | m
      eLen += mLen
      for (; eLen > 0; buffer[offset + i] = e & 0xff, i += d, e /= 256, eLen -= 8) {}
    
      buffer[offset + i - d] |= s * 128
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/uuid/lib/bytesToUuid.js":
    /*!**********************************************!*\
      !*** ./node_modules/uuid/lib/bytesToUuid.js ***!
      \**********************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    /**
     * Convert array of 16 byte values to UUID string format of the form:
     * XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXXXX
     */
    var byteToHex = [];
    for (var i = 0; i < 256; ++i) {
      byteToHex[i] = (i + 0x100).toString(16).substr(1);
    }
    
    function bytesToUuid(buf, offset) {
      var i = offset || 0;
      var bth = byteToHex;
      // join used to fix memory issue caused by concatenation: https://bugs.chromium.org/p/v8/issues/detail?id=3175#c4
      return ([bth[buf[i++]], bth[buf[i++]], 
    	bth[buf[i++]], bth[buf[i++]], '-',
    	bth[buf[i++]], bth[buf[i++]], '-',
    	bth[buf[i++]], bth[buf[i++]], '-',
    	bth[buf[i++]], bth[buf[i++]], '-',
    	bth[buf[i++]], bth[buf[i++]],
    	bth[buf[i++]], bth[buf[i++]],
    	bth[buf[i++]], bth[buf[i++]]]).join('');
    }
    
    module.exports = bytesToUuid;
    
    
    /***/ }),
    
    /***/ "./node_modules/uuid/lib/rng-browser.js":
    /*!**********************************************!*\
      !*** ./node_modules/uuid/lib/rng-browser.js ***!
      \**********************************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    // Unique ID creation requires a high quality random # generator.  In the
    // browser this is a little complicated due to unknown quality of Math.random()
    // and inconsistent support for the `crypto` API.  We do the best we can via
    // feature-detection
    
    // getRandomValues needs to be invoked in a context where "this" is a Crypto
    // implementation. Also, find the complete implementation of crypto on IE11.
    var getRandomValues = (typeof(crypto) != 'undefined' && crypto.getRandomValues && crypto.getRandomValues.bind(crypto)) ||
                          (typeof(msCrypto) != 'undefined' && typeof window.msCrypto.getRandomValues == 'function' && msCrypto.getRandomValues.bind(msCrypto));
    
    if (getRandomValues) {
      // WHATWG crypto RNG - http://wiki.whatwg.org/wiki/Crypto
      var rnds8 = new Uint8Array(16); // eslint-disable-line no-undef
    
      module.exports = function whatwgRNG() {
        getRandomValues(rnds8);
        return rnds8;
      };
    } else {
      // Math.random()-based (RNG)
      //
      // If all else fails, use Math.random().  It's fast, but is of unspecified
      // quality.
      var rnds = new Array(16);
    
      module.exports = function mathRNG() {
        for (var i = 0, r; i < 16; i++) {
          if ((i & 0x03) === 0) r = Math.random() * 0x100000000;
          rnds[i] = r >>> ((i & 0x03) << 3) & 0xff;
        }
    
        return rnds;
      };
    }
    
    
    /***/ }),
    
    /***/ "./node_modules/uuid/v4.js":
    /*!*********************************!*\
      !*** ./node_modules/uuid/v4.js ***!
      \*********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    var rng = __webpack_require__(/*! ./lib/rng */ "./node_modules/uuid/lib/rng-browser.js");
    var bytesToUuid = __webpack_require__(/*! ./lib/bytesToUuid */ "./node_modules/uuid/lib/bytesToUuid.js");
    
    function v4(options, buf, offset) {
      var i = buf && offset || 0;
    
      if (typeof(options) == 'string') {
        buf = options === 'binary' ? new Array(16) : null;
        options = null;
      }
      options = options || {};
    
      var rnds = options.random || (options.rng || rng)();
    
      // Per 4.4, set bits for version and `clock_seq_hi_and_reserved`
      rnds[6] = (rnds[6] & 0x0f) | 0x40;
      rnds[8] = (rnds[8] & 0x3f) | 0x80;
    
      // Copy bytes to buffer, if provided
      if (buf) {
        for (var ii = 0; ii < 16; ++ii) {
          buf[i + ii] = rnds[ii];
        }
      }
    
      return buf || bytesToUuid(rnds);
    }
    
    module.exports = v4;
    
    
    /***/ }),
    
    /***/ "./node_modules/webpack/buildin/global.js":
    /*!***********************************!*\
      !*** (webpack)/buildin/global.js ***!
      \***********************************/
    /*! no static exports found */
    /***/ (function(module, exports) {
    
    var g;
    
    // This works in non-strict mode
    g = (function() {
    	return this;
    })();
    
    try {
    	// This works if eval is allowed (see CSP)
    	g = g || new Function("return this")();
    } catch (e) {
    	// This works if the window reference is available
    	if (typeof window === "object") g = window;
    }
    
    // g can still be undefined, but nothing to do about it...
    // We return undefined, instead of nothing here, so it's
    // easier to handle this case. if(!global) { ...}
    
    module.exports = g;
    
    
    /***/ }),
    
    /***/ "./src/AccessTokenEvents.js":
    /*!**********************************!*\
      !*** ./src/AccessTokenEvents.js ***!
      \**********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.AccessTokenEvents = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _Timer = __webpack_require__(/*! ./Timer.js */ "./src/Timer.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var DefaultAccessTokenExpiringNotificationTime = 60; // seconds
    
    var AccessTokenEvents = exports.AccessTokenEvents = function () {
        function AccessTokenEvents() {
            var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                _ref$accessTokenExpir = _ref.accessTokenExpiringNotificationTime,
                accessTokenExpiringNotificationTime = _ref$accessTokenExpir === undefined ? DefaultAccessTokenExpiringNotificationTime : _ref$accessTokenExpir,
                _ref$accessTokenExpir2 = _ref.accessTokenExpiringTimer,
                accessTokenExpiringTimer = _ref$accessTokenExpir2 === undefined ? new _Timer.Timer("Access token expiring") : _ref$accessTokenExpir2,
                _ref$accessTokenExpir3 = _ref.accessTokenExpiredTimer,
                accessTokenExpiredTimer = _ref$accessTokenExpir3 === undefined ? new _Timer.Timer("Access token expired") : _ref$accessTokenExpir3;
    
            _classCallCheck(this, AccessTokenEvents);
    
            this._accessTokenExpiringNotificationTime = accessTokenExpiringNotificationTime;
    
            this._accessTokenExpiring = accessTokenExpiringTimer;
            this._accessTokenExpired = accessTokenExpiredTimer;
        }
    
        AccessTokenEvents.prototype.load = function load(container) {
            // only register events if there's an access token and it has an expiration
            if (container.access_token && container.expires_in !== undefined) {
                var duration = container.expires_in;
                _Log.Log.debug("AccessTokenEvents.load: access token present, remaining duration:", duration);
    
                if (duration > 0) {
                    // only register expiring if we still have time
                    var expiring = duration - this._accessTokenExpiringNotificationTime;
                    if (expiring <= 0) {
                        expiring = 1;
                    }
    
                    _Log.Log.debug("AccessTokenEvents.load: registering expiring timer in:", expiring);
                    this._accessTokenExpiring.init(expiring);
                } else {
                    _Log.Log.debug("AccessTokenEvents.load: canceling existing expiring timer becase we're past expiration.");
                    this._accessTokenExpiring.cancel();
                }
    
                // if it's negative, it will still fire
                var expired = duration + 1;
                _Log.Log.debug("AccessTokenEvents.load: registering expired timer in:", expired);
                this._accessTokenExpired.init(expired);
            } else {
                this._accessTokenExpiring.cancel();
                this._accessTokenExpired.cancel();
            }
        };
    
        AccessTokenEvents.prototype.unload = function unload() {
            _Log.Log.debug("AccessTokenEvents.unload: canceling existing access token timers");
            this._accessTokenExpiring.cancel();
            this._accessTokenExpired.cancel();
        };
    
        AccessTokenEvents.prototype.addAccessTokenExpiring = function addAccessTokenExpiring(cb) {
            this._accessTokenExpiring.addHandler(cb);
        };
    
        AccessTokenEvents.prototype.removeAccessTokenExpiring = function removeAccessTokenExpiring(cb) {
            this._accessTokenExpiring.removeHandler(cb);
        };
    
        AccessTokenEvents.prototype.addAccessTokenExpired = function addAccessTokenExpired(cb) {
            this._accessTokenExpired.addHandler(cb);
        };
    
        AccessTokenEvents.prototype.removeAccessTokenExpired = function removeAccessTokenExpired(cb) {
            this._accessTokenExpired.removeHandler(cb);
        };
    
        return AccessTokenEvents;
    }();
    
    /***/ }),
    
    /***/ "./src/CheckSessionIFrame.js":
    /*!***********************************!*\
      !*** ./src/CheckSessionIFrame.js ***!
      \***********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.CheckSessionIFrame = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var DefaultInterval = 2000;
    
    var CheckSessionIFrame = exports.CheckSessionIFrame = function () {
        function CheckSessionIFrame(callback, client_id, url, interval) {
            var stopOnError = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : true;
    
            _classCallCheck(this, CheckSessionIFrame);
    
            this._callback = callback;
            this._client_id = client_id;
            this._url = url;
            this._interval = interval || DefaultInterval;
            this._stopOnError = stopOnError;
    
            var idx = url.indexOf("/", url.indexOf("//") + 2);
            this._frame_origin = url.substr(0, idx);
    
            this._frame = window.document.createElement("iframe");
    
            // shotgun approach
            this._frame.style.visibility = "hidden";
            this._frame.style.position = "absolute";
            this._frame.style.display = "none";
            this._frame.style.width = 0;
            this._frame.style.height = 0;
    
            this._frame.src = url;
        }
    
        CheckSessionIFrame.prototype.load = function load() {
            var _this = this;
    
            return new Promise(function (resolve) {
                _this._frame.onload = function () {
                    resolve();
                };
    
                window.document.body.appendChild(_this._frame);
                _this._boundMessageEvent = _this._message.bind(_this);
                window.addEventListener("message", _this._boundMessageEvent, false);
            });
        };
    
        CheckSessionIFrame.prototype._message = function _message(e) {
            if (e.origin === this._frame_origin && e.source === this._frame.contentWindow) {
                if (e.data === "error") {
                    _Log.Log.error("CheckSessionIFrame: error message from check session op iframe");
                    if (this._stopOnError) {
                        this.stop();
                    }
                } else if (e.data === "changed") {
                    _Log.Log.debug("CheckSessionIFrame: changed message from check session op iframe");
                    this.stop();
                    this._callback();
                } else {
                    _Log.Log.debug("CheckSessionIFrame: " + e.data + " message from check session op iframe");
                }
            }
        };
    
        CheckSessionIFrame.prototype.start = function start(session_state) {
            var _this2 = this;
    
            if (this._session_state !== session_state) {
                _Log.Log.debug("CheckSessionIFrame.start");
    
                this.stop();
    
                this._session_state = session_state;
    
                var send = function send() {
                    _this2._frame.contentWindow.postMessage(_this2._client_id + " " + _this2._session_state, _this2._frame_origin);
                };
    
                // trigger now
                send();
    
                // and setup timer
                this._timer = window.setInterval(send, this._interval);
            }
        };
    
        CheckSessionIFrame.prototype.stop = function stop() {
            this._session_state = null;
    
            if (this._timer) {
                _Log.Log.debug("CheckSessionIFrame.stop");
    
                window.clearInterval(this._timer);
                this._timer = null;
            }
        };
    
        return CheckSessionIFrame;
    }();
    
    /***/ }),
    
    /***/ "./src/CordovaIFrameNavigator.js":
    /*!***************************************!*\
      !*** ./src/CordovaIFrameNavigator.js ***!
      \***************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.CordovaIFrameNavigator = undefined;
    
    var _CordovaPopupWindow = __webpack_require__(/*! ./CordovaPopupWindow.js */ "./src/CordovaPopupWindow.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var CordovaIFrameNavigator = exports.CordovaIFrameNavigator = function () {
        function CordovaIFrameNavigator() {
            _classCallCheck(this, CordovaIFrameNavigator);
        }
    
        CordovaIFrameNavigator.prototype.prepare = function prepare(params) {
            params.popupWindowFeatures = 'hidden=yes';
            var popup = new _CordovaPopupWindow.CordovaPopupWindow(params);
            return Promise.resolve(popup);
        };
    
        return CordovaIFrameNavigator;
    }();
    
    /***/ }),
    
    /***/ "./src/CordovaPopupNavigator.js":
    /*!**************************************!*\
      !*** ./src/CordovaPopupNavigator.js ***!
      \**************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.CordovaPopupNavigator = undefined;
    
    var _CordovaPopupWindow = __webpack_require__(/*! ./CordovaPopupWindow.js */ "./src/CordovaPopupWindow.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var CordovaPopupNavigator = exports.CordovaPopupNavigator = function () {
        function CordovaPopupNavigator() {
            _classCallCheck(this, CordovaPopupNavigator);
        }
    
        CordovaPopupNavigator.prototype.prepare = function prepare(params) {
            var popup = new _CordovaPopupWindow.CordovaPopupWindow(params);
            return Promise.resolve(popup);
        };
    
        return CordovaPopupNavigator;
    }();
    
    /***/ }),
    
    /***/ "./src/CordovaPopupWindow.js":
    /*!***********************************!*\
      !*** ./src/CordovaPopupWindow.js ***!
      \***********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.CordovaPopupWindow = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var DefaultPopupFeatures = 'location=no,toolbar=no,zoom=no';
    var DefaultPopupTarget = "_blank";
    
    var CordovaPopupWindow = exports.CordovaPopupWindow = function () {
        function CordovaPopupWindow(params) {
            var _this = this;
    
            _classCallCheck(this, CordovaPopupWindow);
    
            this._promise = new Promise(function (resolve, reject) {
                _this._resolve = resolve;
                _this._reject = reject;
            });
    
            this.features = params.popupWindowFeatures || DefaultPopupFeatures;
            this.target = params.popupWindowTarget || DefaultPopupTarget;
    
            this.redirect_uri = params.startUrl;
            _Log.Log.debug("CordovaPopupWindow.ctor: redirect_uri: " + this.redirect_uri);
        }
    
        CordovaPopupWindow.prototype._isInAppBrowserInstalled = function _isInAppBrowserInstalled(cordovaMetadata) {
            return ["cordova-plugin-inappbrowser", "cordova-plugin-inappbrowser.inappbrowser", "org.apache.cordova.inappbrowser"].some(function (name) {
                return cordovaMetadata.hasOwnProperty(name);
            });
        };
    
        CordovaPopupWindow.prototype.navigate = function navigate(params) {
            if (!params || !params.url) {
                this._error("No url provided");
            } else {
                if (!window.cordova) {
                    return this._error("cordova is undefined");
                }
    
                var cordovaMetadata = window.cordova.require("cordova/plugin_list").metadata;
                if (this._isInAppBrowserInstalled(cordovaMetadata) === false) {
                    return this._error("InAppBrowser plugin not found");
                }
                this._popup = cordova.InAppBrowser.open(params.url, this.target, this.features);
                if (this._popup) {
                    _Log.Log.debug("CordovaPopupWindow.navigate: popup successfully created");
    
                    this._exitCallbackEvent = this._exitCallback.bind(this);
                    this._loadStartCallbackEvent = this._loadStartCallback.bind(this);
    
                    this._popup.addEventListener("exit", this._exitCallbackEvent, false);
                    this._popup.addEventListener("loadstart", this._loadStartCallbackEvent, false);
                } else {
                    this._error("Error opening popup window");
                }
            }
            return this.promise;
        };
    
        CordovaPopupWindow.prototype._loadStartCallback = function _loadStartCallback(event) {
            if (event.url.indexOf(this.redirect_uri) === 0) {
                this._success({ url: event.url });
            }
        };
    
        CordovaPopupWindow.prototype._exitCallback = function _exitCallback(message) {
            this._error(message);
        };
    
        CordovaPopupWindow.prototype._success = function _success(data) {
            this._cleanup();
    
            _Log.Log.debug("CordovaPopupWindow: Successful response from cordova popup window");
            this._resolve(data);
        };
    
        CordovaPopupWindow.prototype._error = function _error(message) {
            this._cleanup();
    
            _Log.Log.error(message);
            this._reject(new Error(message));
        };
    
        CordovaPopupWindow.prototype.close = function close() {
            this._cleanup();
        };
    
        CordovaPopupWindow.prototype._cleanup = function _cleanup() {
            if (this._popup) {
                _Log.Log.debug("CordovaPopupWindow: cleaning up popup");
                this._popup.removeEventListener("exit", this._exitCallbackEvent, false);
                this._popup.removeEventListener("loadstart", this._loadStartCallbackEvent, false);
                this._popup.close();
            }
            this._popup = null;
        };
    
        _createClass(CordovaPopupWindow, [{
            key: 'promise',
            get: function get() {
                return this._promise;
            }
        }]);
    
        return CordovaPopupWindow;
    }();
    
    /***/ }),
    
    /***/ "./src/ErrorResponse.js":
    /*!******************************!*\
      !*** ./src/ErrorResponse.js ***!
      \******************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
            value: true
    });
    exports.ErrorResponse = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
    
    function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var ErrorResponse = exports.ErrorResponse = function (_Error) {
            _inherits(ErrorResponse, _Error);
    
            function ErrorResponse() {
                    var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                        error = _ref.error,
                        error_description = _ref.error_description,
                        error_uri = _ref.error_uri,
                        state = _ref.state,
                        session_state = _ref.session_state;
    
                    _classCallCheck(this, ErrorResponse);
    
                    if (!error) {
                            _Log.Log.error("No error passed to ErrorResponse");
                            throw new Error("error");
                    }
    
                    var _this = _possibleConstructorReturn(this, _Error.call(this, error_description || error));
    
                    _this.name = "ErrorResponse";
    
                    _this.error = error;
                    _this.error_description = error_description;
                    _this.error_uri = error_uri;
    
                    _this.state = state;
                    _this.session_state = session_state;
                    return _this;
            }
    
            return ErrorResponse;
    }(Error);
    
    /***/ }),
    
    /***/ "./src/Event.js":
    /*!**********************!*\
      !*** ./src/Event.js ***!
      \**********************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.Event = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var Event = exports.Event = function () {
        function Event(name) {
            _classCallCheck(this, Event);
    
            this._name = name;
            this._callbacks = [];
        }
    
        Event.prototype.addHandler = function addHandler(cb) {
            this._callbacks.push(cb);
        };
    
        Event.prototype.removeHandler = function removeHandler(cb) {
            var idx = this._callbacks.findIndex(function (item) {
                return item === cb;
            });
            if (idx >= 0) {
                this._callbacks.splice(idx, 1);
            }
        };
    
        Event.prototype.raise = function raise() {
            _Log.Log.debug("Event: Raising event: " + this._name);
            for (var i = 0; i < this._callbacks.length; i++) {
                var _callbacks;
    
                (_callbacks = this._callbacks)[i].apply(_callbacks, arguments);
            }
        };
    
        return Event;
    }();
    
    /***/ }),
    
    /***/ "./src/Global.js":
    /*!***********************!*\
      !*** ./src/Global.js ***!
      \***********************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var timer = {
        setInterval: function (_setInterval) {
            function setInterval(_x, _x2) {
                return _setInterval.apply(this, arguments);
            }
    
            setInterval.toString = function () {
                return _setInterval.toString();
            };
    
            return setInterval;
        }(function (cb, duration) {
            return setInterval(cb, duration);
        }),
        clearInterval: function (_clearInterval) {
            function clearInterval(_x3) {
                return _clearInterval.apply(this, arguments);
            }
    
            clearInterval.toString = function () {
                return _clearInterval.toString();
            };
    
            return clearInterval;
        }(function (handle) {
            return clearInterval(handle);
        })
    };
    
    var testing = false;
    var request = null;
    
    var Global = exports.Global = function () {
        function Global() {
            _classCallCheck(this, Global);
        }
    
        Global._testing = function _testing() {
            testing = true;
        };
    
        Global.setXMLHttpRequest = function setXMLHttpRequest(newRequest) {
            request = newRequest;
        };
    
        _createClass(Global, null, [{
            key: 'location',
            get: function get() {
                if (!testing) {
                    return location;
                }
            }
        }, {
            key: 'localStorage',
            get: function get() {
                if (!testing && typeof window !== 'undefined') {
                    return localStorage;
                }
            }
        }, {
            key: 'sessionStorage',
            get: function get() {
                if (!testing && typeof window !== 'undefined') {
                    return sessionStorage;
                }
            }
        }, {
            key: 'XMLHttpRequest',
            get: function get() {
                if (!testing && typeof window !== 'undefined') {
                    return request || XMLHttpRequest;
                }
            }
        }, {
            key: 'timer',
            get: function get() {
                if (!testing) {
                    return timer;
                }
            }
        }]);
    
        return Global;
    }();
    
    /***/ }),
    
    /***/ "./src/IFrameNavigator.js":
    /*!********************************!*\
      !*** ./src/IFrameNavigator.js ***!
      \********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.IFrameNavigator = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _IFrameWindow = __webpack_require__(/*! ./IFrameWindow.js */ "./src/IFrameWindow.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var IFrameNavigator = exports.IFrameNavigator = function () {
        function IFrameNavigator() {
            _classCallCheck(this, IFrameNavigator);
        }
    
        IFrameNavigator.prototype.prepare = function prepare(params) {
            var frame = new _IFrameWindow.IFrameWindow(params);
            return Promise.resolve(frame);
        };
    
        IFrameNavigator.prototype.callback = function callback(url) {
            _Log.Log.debug("IFrameNavigator.callback");
    
            try {
                _IFrameWindow.IFrameWindow.notifyParent(url);
                return Promise.resolve();
            } catch (e) {
                return Promise.reject(e);
            }
        };
    
        return IFrameNavigator;
    }();
    
    /***/ }),
    
    /***/ "./src/IFrameWindow.js":
    /*!*****************************!*\
      !*** ./src/IFrameWindow.js ***!
      \*****************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.IFrameWindow = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var DefaultTimeout = 10000;
    
    var IFrameWindow = exports.IFrameWindow = function () {
        function IFrameWindow(params) {
            var _this = this;
    
            _classCallCheck(this, IFrameWindow);
    
            this._promise = new Promise(function (resolve, reject) {
                _this._resolve = resolve;
                _this._reject = reject;
            });
    
            this._boundMessageEvent = this._message.bind(this);
            window.addEventListener("message", this._boundMessageEvent, false);
    
            this._frame = window.document.createElement("iframe");
    
            // shotgun approach
            this._frame.style.visibility = "hidden";
            this._frame.style.position = "absolute";
            this._frame.style.display = "none";
            this._frame.style.width = 0;
            this._frame.style.height = 0;
    
            window.document.body.appendChild(this._frame);
        }
    
        IFrameWindow.prototype.navigate = function navigate(params) {
            if (!params || !params.url) {
                this._error("No url provided");
            } else {
                var timeout = params.silentRequestTimeout || DefaultTimeout;
                _Log.Log.debug("IFrameWindow.navigate: Using timeout of:", timeout);
                this._timer = window.setTimeout(this._timeout.bind(this), timeout);
                this._frame.src = params.url;
            }
    
            return this.promise;
        };
    
        IFrameWindow.prototype._success = function _success(data) {
            this._cleanup();
    
            _Log.Log.debug("IFrameWindow: Successful response from frame window");
            this._resolve(data);
        };
    
        IFrameWindow.prototype._error = function _error(message) {
            this._cleanup();
    
            _Log.Log.error(message);
            this._reject(new Error(message));
        };
    
        IFrameWindow.prototype.close = function close() {
            this._cleanup();
        };
    
        IFrameWindow.prototype._cleanup = function _cleanup() {
            if (this._frame) {
                _Log.Log.debug("IFrameWindow: cleanup");
    
                window.removeEventListener("message", this._boundMessageEvent, false);
                window.clearTimeout(this._timer);
                window.document.body.removeChild(this._frame);
    
                this._timer = null;
                this._frame = null;
                this._boundMessageEvent = null;
            }
        };
    
        IFrameWindow.prototype._timeout = function _timeout() {
            _Log.Log.debug("IFrameWindow.timeout");
            this._error("Frame window timed out");
        };
    
        IFrameWindow.prototype._message = function _message(e) {
            _Log.Log.debug("IFrameWindow.message");
    
            if (this._timer && e.origin === this._origin && e.source === this._frame.contentWindow) {
                var url = e.data;
                if (url) {
                    this._success({ url: url });
                } else {
                    this._error("Invalid response from frame");
                }
            }
        };
    
        IFrameWindow.notifyParent = function notifyParent(url) {
            _Log.Log.debug("IFrameWindow.notifyParent");
            if (window.frameElement) {
                url = url || window.location.href;
                if (url) {
                    _Log.Log.debug("IFrameWindow.notifyParent: posting url message to parent");
                    window.parent.postMessage(url, location.protocol + "//" + location.host);
                }
            }
        };
    
        _createClass(IFrameWindow, [{
            key: "promise",
            get: function get() {
                return this._promise;
            }
        }, {
            key: "_origin",
            get: function get() {
                return location.protocol + "//" + location.host;
            }
        }]);
    
        return IFrameWindow;
    }();
    
    /***/ }),
    
    /***/ "./src/InMemoryWebStorage.js":
    /*!***********************************!*\
      !*** ./src/InMemoryWebStorage.js ***!
      \***********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.InMemoryWebStorage = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var InMemoryWebStorage = exports.InMemoryWebStorage = function () {
        function InMemoryWebStorage() {
            _classCallCheck(this, InMemoryWebStorage);
    
            this._data = {};
        }
    
        InMemoryWebStorage.prototype.getItem = function getItem(key) {
            _Log.Log.debug("InMemoryWebStorage.getItem", key);
            return this._data[key];
        };
    
        InMemoryWebStorage.prototype.setItem = function setItem(key, value) {
            _Log.Log.debug("InMemoryWebStorage.setItem", key);
            this._data[key] = value;
        };
    
        InMemoryWebStorage.prototype.removeItem = function removeItem(key) {
            _Log.Log.debug("InMemoryWebStorage.removeItem", key);
            delete this._data[key];
        };
    
        InMemoryWebStorage.prototype.key = function key(index) {
            return Object.getOwnPropertyNames(this._data)[index];
        };
    
        _createClass(InMemoryWebStorage, [{
            key: "length",
            get: function get() {
                return Object.getOwnPropertyNames(this._data).length;
            }
        }]);
    
        return InMemoryWebStorage;
    }();
    
    /***/ }),
    
    /***/ "./src/JoseUtil.js":
    /*!*************************!*\
      !*** ./src/JoseUtil.js ***!
      \*************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    exports.JoseUtil = undefined;
    
    var _jsrsasign = __webpack_require__(/*! ./crypto/jsrsasign */ "./src/crypto/jsrsasign.js");
    
    var _JoseUtilImpl = __webpack_require__(/*! ./JoseUtilImpl */ "./src/JoseUtilImpl.js");
    
    var _JoseUtilImpl2 = _interopRequireDefault(_JoseUtilImpl);
    
    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
    
    var JoseUtil = exports.JoseUtil = (0, _JoseUtilImpl2.default)({ jws: _jsrsasign.jws, KeyUtil: _jsrsasign.KeyUtil, X509: _jsrsasign.X509, crypto: _jsrsasign.crypto, hextob64u: _jsrsasign.hextob64u, b64tohex: _jsrsasign.b64tohex, AllowedSigningAlgs: _jsrsasign.AllowedSigningAlgs });
    
    /***/ }),
    
    /***/ "./src/JoseUtilImpl.js":
    /*!*****************************!*\
      !*** ./src/JoseUtilImpl.js ***!
      \*****************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.default = getJoseUtil;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    function getJoseUtil(_ref) {
        var jws = _ref.jws,
            KeyUtil = _ref.KeyUtil,
            X509 = _ref.X509,
            crypto = _ref.crypto,
            hextob64u = _ref.hextob64u,
            b64tohex = _ref.b64tohex,
            AllowedSigningAlgs = _ref.AllowedSigningAlgs;
    
        return function () {
            function JoseUtil() {
                _classCallCheck(this, JoseUtil);
            }
    
            JoseUtil.parseJwt = function parseJwt(jwt) {
                _Log.Log.debug("JoseUtil.parseJwt");
                try {
                    var token = jws.JWS.parse(jwt);
                    return {
                        header: token.headerObj,
                        payload: token.payloadObj
                    };
                } catch (e) {
                    _Log.Log.error(e);
                }
            };
    
            JoseUtil.validateJwt = function validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive) {
                _Log.Log.debug("JoseUtil.validateJwt");
    
                try {
                    if (key.kty === "RSA") {
                        if (key.e && key.n) {
                            key = KeyUtil.getKey(key);
                        } else if (key.x5c && key.x5c.length) {
                            var hex = b64tohex(key.x5c[0]);
                            key = X509.getPublicKeyFromCertHex(hex);
                        } else {
                            _Log.Log.error("JoseUtil.validateJwt: RSA key missing key material", key);
                            return Promise.reject(new Error("RSA key missing key material"));
                        }
                    } else if (key.kty === "EC") {
                        if (key.crv && key.x && key.y) {
                            key = KeyUtil.getKey(key);
                        } else {
                            _Log.Log.error("JoseUtil.validateJwt: EC key missing key material", key);
                            return Promise.reject(new Error("EC key missing key material"));
                        }
                    } else {
                        _Log.Log.error("JoseUtil.validateJwt: Unsupported key type", key && key.kty);
                        return Promise.reject(new Error( true && key.kty));
                    }
    
                    return JoseUtil._validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive);
                } catch (e) {
                    _Log.Log.error(e && e.message || e);
                    return Promise.reject("JWT validation failed");
                }
            };
    
            JoseUtil.validateJwtAttributes = function validateJwtAttributes(jwt, issuer, audience, clockSkew, now, timeInsensitive) {
                if (!clockSkew) {
                    clockSkew = 0;
                }
    
                if (!now) {
                    now = parseInt(Date.now() / 1000);
                }
    
                var payload = JoseUtil.parseJwt(jwt).payload;
    
                if (!payload.iss) {
                    _Log.Log.error("JoseUtil._validateJwt: issuer was not provided");
                    return Promise.reject(new Error("issuer was not provided"));
                }
                if (payload.iss !== issuer) {
                    _Log.Log.error("JoseUtil._validateJwt: Invalid issuer in token", payload.iss);
                    return Promise.reject(new Error("Invalid issuer in token: " + payload.iss));
                }
    
                if (!payload.aud) {
                    _Log.Log.error("JoseUtil._validateJwt: aud was not provided");
                    return Promise.reject(new Error("aud was not provided"));
                }
                var validAudience = payload.aud === audience || Array.isArray(payload.aud) && payload.aud.indexOf(audience) >= 0;
                if (!validAudience) {
                    _Log.Log.error("JoseUtil._validateJwt: Invalid audience in token", payload.aud);
                    return Promise.reject(new Error("Invalid audience in token: " + payload.aud));
                }
                if (payload.azp && payload.azp !== audience) {
                    _Log.Log.error("JoseUtil._validateJwt: Invalid azp in token", payload.azp);
                    return Promise.reject(new Error("Invalid azp in token: " + payload.azp));
                }
    
                if (!timeInsensitive) {
                    var lowerNow = now + clockSkew;
                    var upperNow = now - clockSkew;
    
                    if (!payload.iat) {
                        _Log.Log.error("JoseUtil._validateJwt: iat was not provided");
                        return Promise.reject(new Error("iat was not provided"));
                    }
                    if (lowerNow < payload.iat) {
                        _Log.Log.error("JoseUtil._validateJwt: iat is in the future", payload.iat);
                        return Promise.reject(new Error("iat is in the future: " + payload.iat));
                    }
    
                    if (payload.nbf && lowerNow < payload.nbf) {
                        _Log.Log.error("JoseUtil._validateJwt: nbf is in the future", payload.nbf);
                        return Promise.reject(new Error("nbf is in the future: " + payload.nbf));
                    }
    
                    if (!payload.exp) {
                        _Log.Log.error("JoseUtil._validateJwt: exp was not provided");
                        return Promise.reject(new Error("exp was not provided"));
                    }
                    if (payload.exp < upperNow) {
                        _Log.Log.error("JoseUtil._validateJwt: exp is in the past", payload.exp);
                        return Promise.reject(new Error("exp is in the past:" + payload.exp));
                    }
                }
    
                return Promise.resolve(payload);
            };
    
            JoseUtil._validateJwt = function _validateJwt(jwt, key, issuer, audience, clockSkew, now, timeInsensitive) {
    
                return JoseUtil.validateJwtAttributes(jwt, issuer, audience, clockSkew, now, timeInsensitive).then(function (payload) {
                    try {
                        if (!jws.JWS.verify(jwt, key, AllowedSigningAlgs)) {
                            _Log.Log.error("JoseUtil._validateJwt: signature validation failed");
                            return Promise.reject(new Error("signature validation failed"));
                        }
    
                        return payload;
                    } catch (e) {
                        _Log.Log.error(e && e.message || e);
                        return Promise.reject(new Error("signature validation failed"));
                    }
                });
            };
    
            JoseUtil.hashString = function hashString(value, alg) {
                try {
                    return crypto.Util.hashString(value, alg);
                } catch (e) {
                    _Log.Log.error(e);
                }
            };
    
            JoseUtil.hexToBase64Url = function hexToBase64Url(value) {
                try {
                    return hextob64u(value);
                } catch (e) {
                    _Log.Log.error(e);
                }
            };
    
            return JoseUtil;
        }();
    }
    module.exports = exports["default"];
    
    /***/ }),
    
    /***/ "./src/JsonService.js":
    /*!****************************!*\
      !*** ./src/JsonService.js ***!
      \****************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.JsonService = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _Global = __webpack_require__(/*! ./Global.js */ "./src/Global.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var JsonService = exports.JsonService = function () {
        function JsonService() {
            var additionalContentTypes = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
            var XMLHttpRequestCtor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _Global.Global.XMLHttpRequest;
            var jwtHandler = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : null;
    
            _classCallCheck(this, JsonService);
    
            if (additionalContentTypes && Array.isArray(additionalContentTypes)) {
                this._contentTypes = additionalContentTypes.slice();
            } else {
                this._contentTypes = [];
            }
            this._contentTypes.push('application/json');
            if (jwtHandler) {
                this._contentTypes.push('application/jwt');
            }
    
            this._XMLHttpRequest = XMLHttpRequestCtor;
            this._jwtHandler = jwtHandler;
        }
    
        JsonService.prototype.getJson = function getJson(url, token) {
            var _this = this;
    
            if (!url) {
                _Log.Log.error("JsonService.getJson: No url passed");
                throw new Error("url");
            }
    
            _Log.Log.debug("JsonService.getJson, url: ", url);
    
            return new Promise(function (resolve, reject) {
    
                var req = new _this._XMLHttpRequest();
                req.open('GET', url);
    
                var allowedContentTypes = _this._contentTypes;
                var jwtHandler = _this._jwtHandler;
    
                req.onload = function () {
                    _Log.Log.debug("JsonService.getJson: HTTP response received, status", req.status);
    
                    if (req.status === 200) {
    
                        var contentType = req.getResponseHeader("Content-Type");
                        if (contentType) {
    
                            var found = allowedContentTypes.find(function (item) {
                                if (contentType.startsWith(item)) {
                                    return true;
                                }
                            });
    
                            if (found == "application/jwt") {
                                jwtHandler(req).then(resolve, reject);
                                return;
                            }
    
                            if (found) {
                                try {
                                    resolve(JSON.parse(req.responseText));
                                    return;
                                } catch (e) {
                                    _Log.Log.error("JsonService.getJson: Error parsing JSON response", e.message);
                                    reject(e);
                                    return;
                                }
                            }
                        }
    
                        reject(Error("Invalid response Content-Type: " + contentType + ", from URL: " + url));
                    } else {
                        reject(Error(req.statusText + " (" + req.status + ")"));
                    }
                };
    
                req.onerror = function () {
                    _Log.Log.error("JsonService.getJson: network error");
                    reject(Error("Network Error"));
                };
    
                if (token) {
                    _Log.Log.debug("JsonService.getJson: token passed, setting Authorization header");
                    req.setRequestHeader("Authorization", "Bearer " + token);
                }
    
                req.send();
            });
        };
    
        JsonService.prototype.postForm = function postForm(url, payload) {
            var _this2 = this;
    
            if (!url) {
                _Log.Log.error("JsonService.postForm: No url passed");
                throw new Error("url");
            }
    
            _Log.Log.debug("JsonService.postForm, url: ", url);
    
            return new Promise(function (resolve, reject) {
    
                var req = new _this2._XMLHttpRequest();
                req.open('POST', url);
    
                var allowedContentTypes = _this2._contentTypes;
    
                req.onload = function () {
                    _Log.Log.debug("JsonService.postForm: HTTP response received, status", req.status);
    
                    if (req.status === 200) {
    
                        var contentType = req.getResponseHeader("Content-Type");
                        if (contentType) {
    
                            var found = allowedContentTypes.find(function (item) {
                                if (contentType.startsWith(item)) {
                                    return true;
                                }
                            });
    
                            if (found) {
                                try {
                                    resolve(JSON.parse(req.responseText));
                                    return;
                                } catch (e) {
                                    _Log.Log.error("JsonService.postForm: Error parsing JSON response", e.message);
                                    reject(e);
                                    return;
                                }
                            }
                        }
    
                        reject(Error("Invalid response Content-Type: " + contentType + ", from URL: " + url));
                        return;
                    }
    
                    if (req.status === 400) {
    
                        var contentType = req.getResponseHeader("Content-Type");
                        if (contentType) {
    
                            var found = allowedContentTypes.find(function (item) {
                                if (contentType.startsWith(item)) {
                                    return true;
                                }
                            });
    
                            if (found) {
                                try {
                                    var payload = JSON.parse(req.responseText);
                                    if (payload && payload.error) {
                                        _Log.Log.error("JsonService.postForm: Error from server: ", payload.error);
                                        reject(new Error(payload.error));
                                        return;
                                    }
                                } catch (e) {
                                    _Log.Log.error("JsonService.postForm: Error parsing JSON response", e.message);
                                    reject(e);
                                    return;
                                }
                            }
                        }
                    }
    
                    reject(Error(req.statusText + " (" + req.status + ")"));
                };
    
                req.onerror = function () {
                    _Log.Log.error("JsonService.postForm: network error");
                    reject(Error("Network Error"));
                };
    
                var body = "";
                for (var key in payload) {
    
                    var value = payload[key];
    
                    if (value) {
    
                        if (body.length > 0) {
                            body += "&";
                        }
    
                        body += encodeURIComponent(key);
                        body += "=";
                        body += encodeURIComponent(value);
                    }
                }
    
                req.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                req.send(body);
            });
        };
    
        return JsonService;
    }();
    
    /***/ }),
    
    /***/ "./src/Log.js":
    /*!********************!*\
      !*** ./src/Log.js ***!
      \********************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var nopLogger = {
        debug: function debug() {},
        info: function info() {},
        warn: function warn() {},
        error: function error() {}
    };
    
    var NONE = 0;
    var ERROR = 1;
    var WARN = 2;
    var INFO = 3;
    var DEBUG = 4;
    
    var logger = void 0;
    var level = void 0;
    
    var Log = exports.Log = function () {
        function Log() {
            _classCallCheck(this, Log);
        }
    
        Log.reset = function reset() {
            level = INFO;
            logger = nopLogger;
        };
    
        Log.debug = function debug() {
            if (level >= DEBUG) {
                for (var _len = arguments.length, args = Array(_len), _key = 0; _key < _len; _key++) {
                    args[_key] = arguments[_key];
                }
    
                logger.debug.apply(logger, Array.from(args));
            }
        };
    
        Log.info = function info() {
            if (level >= INFO) {
                for (var _len2 = arguments.length, args = Array(_len2), _key2 = 0; _key2 < _len2; _key2++) {
                    args[_key2] = arguments[_key2];
                }
    
                logger.info.apply(logger, Array.from(args));
            }
        };
    
        Log.warn = function warn() {
            if (level >= WARN) {
                for (var _len3 = arguments.length, args = Array(_len3), _key3 = 0; _key3 < _len3; _key3++) {
                    args[_key3] = arguments[_key3];
                }
    
                logger.warn.apply(logger, Array.from(args));
            }
        };
    
        Log.error = function error() {
            if (level >= ERROR) {
                for (var _len4 = arguments.length, args = Array(_len4), _key4 = 0; _key4 < _len4; _key4++) {
                    args[_key4] = arguments[_key4];
                }
    
                logger.error.apply(logger, Array.from(args));
            }
        };
    
        _createClass(Log, null, [{
            key: "NONE",
            get: function get() {
                return NONE;
            }
        }, {
            key: "ERROR",
            get: function get() {
                return ERROR;
            }
        }, {
            key: "WARN",
            get: function get() {
                return WARN;
            }
        }, {
            key: "INFO",
            get: function get() {
                return INFO;
            }
        }, {
            key: "DEBUG",
            get: function get() {
                return DEBUG;
            }
        }, {
            key: "level",
            get: function get() {
                return level;
            },
            set: function set(value) {
                if (NONE <= value && value <= DEBUG) {
                    level = value;
                } else {
                    throw new Error("Invalid log level");
                }
            }
        }, {
            key: "logger",
            get: function get() {
                return logger;
            },
            set: function set(value) {
                if (!value.debug && value.info) {
                    // just to stay backwards compat. can remove in 2.0
                    value.debug = value.info;
                }
    
                if (value.debug && value.info && value.warn && value.error) {
                    logger = value;
                } else {
                    throw new Error("Invalid logger");
                }
            }
        }]);
    
        return Log;
    }();
    
    Log.reset();
    
    /***/ }),
    
    /***/ "./src/MetadataService.js":
    /*!********************************!*\
      !*** ./src/MetadataService.js ***!
      \********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.MetadataService = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _JsonService = __webpack_require__(/*! ./JsonService.js */ "./src/JsonService.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var OidcMetadataUrlPath = '.well-known/openid-configuration';
    
    var MetadataService = exports.MetadataService = function () {
        function MetadataService(settings) {
            var JsonServiceCtor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _JsonService.JsonService;
    
            _classCallCheck(this, MetadataService);
    
            if (!settings) {
                _Log.Log.error("MetadataService: No settings passed to MetadataService");
                throw new Error("settings");
            }
    
            this._settings = settings;
            this._jsonService = new JsonServiceCtor(['application/jwk-set+json']);
        }
    
        MetadataService.prototype.getMetadata = function getMetadata() {
            var _this = this;
    
            if (this._settings.metadata) {
                _Log.Log.debug("MetadataService.getMetadata: Returning metadata from settings");
                return Promise.resolve(this._settings.metadata);
            }
    
            if (!this.metadataUrl) {
                _Log.Log.error("MetadataService.getMetadata: No authority or metadataUrl configured on settings");
                return Promise.reject(new Error("No authority or metadataUrl configured on settings"));
            }
    
            _Log.Log.debug("MetadataService.getMetadata: getting metadata from", this.metadataUrl);
    
            return this._jsonService.getJson(this.metadataUrl).then(function (metadata) {
                _Log.Log.debug("MetadataService.getMetadata: json received");
                _this._settings.metadata = metadata;
                return metadata;
            });
        };
    
        MetadataService.prototype.getIssuer = function getIssuer() {
            return this._getMetadataProperty("issuer");
        };
    
        MetadataService.prototype.getAuthorizationEndpoint = function getAuthorizationEndpoint() {
            return this._getMetadataProperty("authorization_endpoint");
        };
    
        MetadataService.prototype.getUserInfoEndpoint = function getUserInfoEndpoint() {
            return this._getMetadataProperty("userinfo_endpoint");
        };
    
        MetadataService.prototype.getTokenEndpoint = function getTokenEndpoint() {
            var optional = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true;
    
            return this._getMetadataProperty("token_endpoint", optional);
        };
    
        MetadataService.prototype.getCheckSessionIframe = function getCheckSessionIframe() {
            return this._getMetadataProperty("check_session_iframe", true);
        };
    
        MetadataService.prototype.getEndSessionEndpoint = function getEndSessionEndpoint() {
            return this._getMetadataProperty("end_session_endpoint", true);
        };
    
        MetadataService.prototype.getRevocationEndpoint = function getRevocationEndpoint() {
            return this._getMetadataProperty("revocation_endpoint", true);
        };
    
        MetadataService.prototype.getKeysEndpoint = function getKeysEndpoint() {
            return this._getMetadataProperty("jwks_uri", true);
        };
    
        MetadataService.prototype._getMetadataProperty = function _getMetadataProperty(name) {
            var optional = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : false;
    
            _Log.Log.debug("MetadataService.getMetadataProperty for: " + name);
    
            return this.getMetadata().then(function (metadata) {
                _Log.Log.debug("MetadataService.getMetadataProperty: metadata recieved");
    
                if (metadata[name] === undefined) {
    
                    if (optional === true) {
                        _Log.Log.warn("MetadataService.getMetadataProperty: Metadata does not contain optional property " + name);
                        return undefined;
                    } else {
                        _Log.Log.error("MetadataService.getMetadataProperty: Metadata does not contain property " + name);
                        throw new Error("Metadata does not contain property " + name);
                    }
                }
    
                return metadata[name];
            });
        };
    
        MetadataService.prototype.getSigningKeys = function getSigningKeys() {
            var _this2 = this;
    
            if (this._settings.signingKeys) {
                _Log.Log.debug("MetadataService.getSigningKeys: Returning signingKeys from settings");
                return Promise.resolve(this._settings.signingKeys);
            }
    
            return this._getMetadataProperty("jwks_uri").then(function (jwks_uri) {
                _Log.Log.debug("MetadataService.getSigningKeys: jwks_uri received", jwks_uri);
    
                return _this2._jsonService.getJson(jwks_uri).then(function (keySet) {
                    _Log.Log.debug("MetadataService.getSigningKeys: key set received", keySet);
    
                    if (!keySet.keys) {
                        _Log.Log.error("MetadataService.getSigningKeys: Missing keys on keyset");
                        throw new Error("Missing keys on keyset");
                    }
    
                    _this2._settings.signingKeys = keySet.keys;
                    return _this2._settings.signingKeys;
                });
            });
        };
    
        _createClass(MetadataService, [{
            key: 'metadataUrl',
            get: function get() {
                if (!this._metadataUrl) {
                    if (this._settings.metadataUrl) {
                        this._metadataUrl = this._settings.metadataUrl;
                    } else {
                        this._metadataUrl = this._settings.authority;
    
                        if (this._metadataUrl && this._metadataUrl.indexOf(OidcMetadataUrlPath) < 0) {
                            if (this._metadataUrl[this._metadataUrl.length - 1] !== '/') {
                                this._metadataUrl += '/';
                            }
                            this._metadataUrl += OidcMetadataUrlPath;
                        }
                    }
                }
    
                return this._metadataUrl;
            }
        }]);
    
        return MetadataService;
    }();
    
    /***/ }),
    
    /***/ "./src/OidcClient.js":
    /*!***************************!*\
      !*** ./src/OidcClient.js ***!
      \***************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.OidcClient = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _OidcClientSettings = __webpack_require__(/*! ./OidcClientSettings.js */ "./src/OidcClientSettings.js");
    
    var _ErrorResponse = __webpack_require__(/*! ./ErrorResponse.js */ "./src/ErrorResponse.js");
    
    var _SigninRequest = __webpack_require__(/*! ./SigninRequest.js */ "./src/SigninRequest.js");
    
    var _SigninResponse = __webpack_require__(/*! ./SigninResponse.js */ "./src/SigninResponse.js");
    
    var _SignoutRequest = __webpack_require__(/*! ./SignoutRequest.js */ "./src/SignoutRequest.js");
    
    var _SignoutResponse = __webpack_require__(/*! ./SignoutResponse.js */ "./src/SignoutResponse.js");
    
    var _SigninState = __webpack_require__(/*! ./SigninState.js */ "./src/SigninState.js");
    
    var _State = __webpack_require__(/*! ./State.js */ "./src/State.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var OidcClient = exports.OidcClient = function () {
        function OidcClient() {
            var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            _classCallCheck(this, OidcClient);
    
            if (settings instanceof _OidcClientSettings.OidcClientSettings) {
                this._settings = settings;
            } else {
                this._settings = new _OidcClientSettings.OidcClientSettings(settings);
            }
        }
    
        OidcClient.prototype.createSigninRequest = function createSigninRequest() {
            var _this = this;
    
            var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                response_type = _ref.response_type,
                scope = _ref.scope,
                redirect_uri = _ref.redirect_uri,
                data = _ref.data,
                state = _ref.state,
                prompt = _ref.prompt,
                display = _ref.display,
                max_age = _ref.max_age,
                ui_locales = _ref.ui_locales,
                id_token_hint = _ref.id_token_hint,
                login_hint = _ref.login_hint,
                acr_values = _ref.acr_values,
                resource = _ref.resource,
                request = _ref.request,
                request_uri = _ref.request_uri,
                response_mode = _ref.response_mode,
                extraQueryParams = _ref.extraQueryParams,
                extraTokenParams = _ref.extraTokenParams,
                request_type = _ref.request_type,
                skipUserInfo = _ref.skipUserInfo;
    
            var stateStore = arguments[1];
    
            _Log.Log.debug("OidcClient.createSigninRequest");
    
            var client_id = this._settings.client_id;
            response_type = response_type || this._settings.response_type;
            scope = scope || this._settings.scope;
            redirect_uri = redirect_uri || this._settings.redirect_uri;
    
            // id_token_hint, login_hint aren't allowed on _settings
            prompt = prompt || this._settings.prompt;
            display = display || this._settings.display;
            max_age = max_age || this._settings.max_age;
            ui_locales = ui_locales || this._settings.ui_locales;
            acr_values = acr_values || this._settings.acr_values;
            resource = resource || this._settings.resource;
            response_mode = response_mode || this._settings.response_mode;
            extraQueryParams = extraQueryParams || this._settings.extraQueryParams;
            extraTokenParams = extraTokenParams || this._settings.extraTokenParams;
    
            var authority = this._settings.authority;
    
            if (_SigninRequest.SigninRequest.isCode(response_type) && response_type !== "code") {
                return Promise.reject(new Error("OpenID Connect hybrid flow is not supported"));
            }
    
            return this._metadataService.getAuthorizationEndpoint().then(function (url) {
                _Log.Log.debug("OidcClient.createSigninRequest: Received authorization endpoint", url);
    
                var signinRequest = new _SigninRequest.SigninRequest({
                    url: url,
                    client_id: client_id,
                    redirect_uri: redirect_uri,
                    response_type: response_type,
                    scope: scope,
                    data: data || state,
                    authority: authority,
                    prompt: prompt, display: display, max_age: max_age, ui_locales: ui_locales, id_token_hint: id_token_hint, login_hint: login_hint, acr_values: acr_values,
                    resource: resource, request: request, request_uri: request_uri, extraQueryParams: extraQueryParams, extraTokenParams: extraTokenParams, request_type: request_type, response_mode: response_mode,
                    client_secret: _this._settings.client_secret,
                    skipUserInfo: skipUserInfo
                });
    
                var signinState = signinRequest.state;
                stateStore = stateStore || _this._stateStore;
    
                return stateStore.set(signinState.id, signinState.toStorageString()).then(function () {
                    return signinRequest;
                });
            });
        };
    
        OidcClient.prototype.readSigninResponseState = function readSigninResponseState(url, stateStore) {
            var removeState = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    
            _Log.Log.debug("OidcClient.readSigninResponseState");
    
            var useQuery = this._settings.response_mode === "query" || !this._settings.response_mode && _SigninRequest.SigninRequest.isCode(this._settings.response_type);
            var delimiter = useQuery ? "?" : "#";
    
            var response = new _SigninResponse.SigninResponse(url, delimiter);
    
            if (!response.state) {
                _Log.Log.error("OidcClient.readSigninResponseState: No state in response");
                return Promise.reject(new Error("No state in response"));
            }
    
            stateStore = stateStore || this._stateStore;
    
            var stateApi = removeState ? stateStore.remove.bind(stateStore) : stateStore.get.bind(stateStore);
    
            return stateApi(response.state).then(function (storedStateString) {
                if (!storedStateString) {
                    _Log.Log.error("OidcClient.readSigninResponseState: No matching state found in storage");
                    throw new Error("No matching state found in storage");
                }
    
                var state = _SigninState.SigninState.fromStorageString(storedStateString);
                return { state: state, response: response };
            });
        };
    
        OidcClient.prototype.processSigninResponse = function processSigninResponse(url, stateStore) {
            var _this2 = this;
    
            _Log.Log.debug("OidcClient.processSigninResponse");
    
            return this.readSigninResponseState(url, stateStore, true).then(function (_ref2) {
                var state = _ref2.state,
                    response = _ref2.response;
    
                _Log.Log.debug("OidcClient.processSigninResponse: Received state from storage; validating response");
                return _this2._validator.validateSigninResponse(state, response);
            });
        };
    
        OidcClient.prototype.createSignoutRequest = function createSignoutRequest() {
            var _this3 = this;
    
            var _ref3 = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                id_token_hint = _ref3.id_token_hint,
                data = _ref3.data,
                state = _ref3.state,
                post_logout_redirect_uri = _ref3.post_logout_redirect_uri,
                extraQueryParams = _ref3.extraQueryParams,
                request_type = _ref3.request_type;
    
            var stateStore = arguments[1];
    
            _Log.Log.debug("OidcClient.createSignoutRequest");
    
            post_logout_redirect_uri = post_logout_redirect_uri || this._settings.post_logout_redirect_uri;
            extraQueryParams = extraQueryParams || this._settings.extraQueryParams;
    
            return this._metadataService.getEndSessionEndpoint().then(function (url) {
                if (!url) {
                    _Log.Log.error("OidcClient.createSignoutRequest: No end session endpoint url returned");
                    throw new Error("no end session endpoint");
                }
    
                _Log.Log.debug("OidcClient.createSignoutRequest: Received end session endpoint", url);
    
                var request = new _SignoutRequest.SignoutRequest({
                    url: url,
                    id_token_hint: id_token_hint,
                    post_logout_redirect_uri: post_logout_redirect_uri,
                    data: data || state,
                    extraQueryParams: extraQueryParams,
                    request_type: request_type
                });
    
                var signoutState = request.state;
                if (signoutState) {
                    _Log.Log.debug("OidcClient.createSignoutRequest: Signout request has state to persist");
    
                    stateStore = stateStore || _this3._stateStore;
                    stateStore.set(signoutState.id, signoutState.toStorageString());
                }
    
                return request;
            });
        };
    
        OidcClient.prototype.readSignoutResponseState = function readSignoutResponseState(url, stateStore) {
            var removeState = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
    
            _Log.Log.debug("OidcClient.readSignoutResponseState");
    
            var response = new _SignoutResponse.SignoutResponse(url);
            if (!response.state) {
                _Log.Log.debug("OidcClient.readSignoutResponseState: No state in response");
    
                if (response.error) {
                    _Log.Log.warn("OidcClient.readSignoutResponseState: Response was error: ", response.error);
                    return Promise.reject(new _ErrorResponse.ErrorResponse(response));
                }
    
                return Promise.resolve({ undefined: undefined, response: response });
            }
    
            var stateKey = response.state;
    
            stateStore = stateStore || this._stateStore;
    
            var stateApi = removeState ? stateStore.remove.bind(stateStore) : stateStore.get.bind(stateStore);
            return stateApi(stateKey).then(function (storedStateString) {
                if (!storedStateString) {
                    _Log.Log.error("OidcClient.readSignoutResponseState: No matching state found in storage");
                    throw new Error("No matching state found in storage");
                }
    
                var state = _State.State.fromStorageString(storedStateString);
    
                return { state: state, response: response };
            });
        };
    
        OidcClient.prototype.processSignoutResponse = function processSignoutResponse(url, stateStore) {
            var _this4 = this;
    
            _Log.Log.debug("OidcClient.processSignoutResponse");
    
            return this.readSignoutResponseState(url, stateStore, true).then(function (_ref4) {
                var state = _ref4.state,
                    response = _ref4.response;
    
                if (state) {
                    _Log.Log.debug("OidcClient.processSignoutResponse: Received state from storage; validating response");
                    return _this4._validator.validateSignoutResponse(state, response);
                } else {
                    _Log.Log.debug("OidcClient.processSignoutResponse: No state from storage; skipping validating response");
                    return response;
                }
            });
        };
    
        OidcClient.prototype.clearStaleState = function clearStaleState(stateStore) {
            _Log.Log.debug("OidcClient.clearStaleState");
    
            stateStore = stateStore || this._stateStore;
    
            return _State.State.clearStaleState(stateStore, this.settings.staleStateAge);
        };
    
        _createClass(OidcClient, [{
            key: '_stateStore',
            get: function get() {
                return this.settings.stateStore;
            }
        }, {
            key: '_validator',
            get: function get() {
                return this.settings.validator;
            }
        }, {
            key: '_metadataService',
            get: function get() {
                return this.settings.metadataService;
            }
        }, {
            key: 'settings',
            get: function get() {
                return this._settings;
            }
        }, {
            key: 'metadataService',
            get: function get() {
                return this._metadataService;
            }
        }]);
    
        return OidcClient;
    }();
    
    /***/ }),
    
    /***/ "./src/OidcClientSettings.js":
    /*!***********************************!*\
      !*** ./src/OidcClientSettings.js ***!
      \***********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.OidcClientSettings = undefined;
    
    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _WebStorageStateStore = __webpack_require__(/*! ./WebStorageStateStore.js */ "./src/WebStorageStateStore.js");
    
    var _ResponseValidator = __webpack_require__(/*! ./ResponseValidator.js */ "./src/ResponseValidator.js");
    
    var _MetadataService = __webpack_require__(/*! ./MetadataService.js */ "./src/MetadataService.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var OidcMetadataUrlPath = '.well-known/openid-configuration';
    
    var DefaultResponseType = "id_token";
    var DefaultScope = "openid";
    var DefaultStaleStateAge = 60 * 15; // seconds
    var DefaultClockSkewInSeconds = 60 * 5;
    
    var OidcClientSettings = exports.OidcClientSettings = function () {
        function OidcClientSettings() {
            var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                authority = _ref.authority,
                metadataUrl = _ref.metadataUrl,
                metadata = _ref.metadata,
                signingKeys = _ref.signingKeys,
                client_id = _ref.client_id,
                client_secret = _ref.client_secret,
                _ref$response_type = _ref.response_type,
                response_type = _ref$response_type === undefined ? DefaultResponseType : _ref$response_type,
                _ref$scope = _ref.scope,
                scope = _ref$scope === undefined ? DefaultScope : _ref$scope,
                redirect_uri = _ref.redirect_uri,
                post_logout_redirect_uri = _ref.post_logout_redirect_uri,
                prompt = _ref.prompt,
                display = _ref.display,
                max_age = _ref.max_age,
                ui_locales = _ref.ui_locales,
                acr_values = _ref.acr_values,
                resource = _ref.resource,
                response_mode = _ref.response_mode,
                _ref$filterProtocolCl = _ref.filterProtocolClaims,
                filterProtocolClaims = _ref$filterProtocolCl === undefined ? true : _ref$filterProtocolCl,
                _ref$loadUserInfo = _ref.loadUserInfo,
                loadUserInfo = _ref$loadUserInfo === undefined ? true : _ref$loadUserInfo,
                _ref$staleStateAge = _ref.staleStateAge,
                staleStateAge = _ref$staleStateAge === undefined ? DefaultStaleStateAge : _ref$staleStateAge,
                _ref$clockSkew = _ref.clockSkew,
                clockSkew = _ref$clockSkew === undefined ? DefaultClockSkewInSeconds : _ref$clockSkew,
                _ref$userInfoJwtIssue = _ref.userInfoJwtIssuer,
                userInfoJwtIssuer = _ref$userInfoJwtIssue === undefined ? 'OP' : _ref$userInfoJwtIssue,
                _ref$stateStore = _ref.stateStore,
                stateStore = _ref$stateStore === undefined ? new _WebStorageStateStore.WebStorageStateStore() : _ref$stateStore,
                _ref$ResponseValidato = _ref.ResponseValidatorCtor,
                ResponseValidatorCtor = _ref$ResponseValidato === undefined ? _ResponseValidator.ResponseValidator : _ref$ResponseValidato,
                _ref$MetadataServiceC = _ref.MetadataServiceCtor,
                MetadataServiceCtor = _ref$MetadataServiceC === undefined ? _MetadataService.MetadataService : _ref$MetadataServiceC,
                _ref$extraQueryParams = _ref.extraQueryParams,
                extraQueryParams = _ref$extraQueryParams === undefined ? {} : _ref$extraQueryParams,
                _ref$extraTokenParams = _ref.extraTokenParams,
                extraTokenParams = _ref$extraTokenParams === undefined ? {} : _ref$extraTokenParams;
    
            _classCallCheck(this, OidcClientSettings);
    
            this._authority = authority;
            this._metadataUrl = metadataUrl;
            this._metadata = metadata;
            this._signingKeys = signingKeys;
    
            this._client_id = client_id;
            this._client_secret = client_secret;
            this._response_type = response_type;
            this._scope = scope;
            this._redirect_uri = redirect_uri;
            this._post_logout_redirect_uri = post_logout_redirect_uri;
    
            this._prompt = prompt;
            this._display = display;
            this._max_age = max_age;
            this._ui_locales = ui_locales;
            this._acr_values = acr_values;
            this._resource = resource;
            this._response_mode = response_mode;
    
            this._filterProtocolClaims = !!filterProtocolClaims;
            this._loadUserInfo = !!loadUserInfo;
            this._staleStateAge = staleStateAge;
            this._clockSkew = clockSkew;
            this._userInfoJwtIssuer = userInfoJwtIssuer;
    
            this._stateStore = stateStore;
            this._validator = new ResponseValidatorCtor(this);
            this._metadataService = new MetadataServiceCtor(this);
    
            this._extraQueryParams = (typeof extraQueryParams === 'undefined' ? 'undefined' : _typeof(extraQueryParams)) === 'object' ? extraQueryParams : {};
            this._extraTokenParams = (typeof extraTokenParams === 'undefined' ? 'undefined' : _typeof(extraTokenParams)) === 'object' ? extraTokenParams : {};
        }
    
        // client config
    
    
        _createClass(OidcClientSettings, [{
            key: 'client_id',
            get: function get() {
                return this._client_id;
            },
            set: function set(value) {
                if (!this._client_id) {
                    // one-time set only
                    this._client_id = value;
                } else {
                    _Log.Log.error("OidcClientSettings.set_client_id: client_id has already been assigned.");
                    throw new Error("client_id has already been assigned.");
                }
            }
        }, {
            key: 'client_secret',
            get: function get() {
                return this._client_secret;
            }
        }, {
            key: 'response_type',
            get: function get() {
                return this._response_type;
            }
        }, {
            key: 'scope',
            get: function get() {
                return this._scope;
            }
        }, {
            key: 'redirect_uri',
            get: function get() {
                return this._redirect_uri;
            }
        }, {
            key: 'post_logout_redirect_uri',
            get: function get() {
                return this._post_logout_redirect_uri;
            }
    
            // optional protocol params
    
        }, {
            key: 'prompt',
            get: function get() {
                return this._prompt;
            }
        }, {
            key: 'display',
            get: function get() {
                return this._display;
            }
        }, {
            key: 'max_age',
            get: function get() {
                return this._max_age;
            }
        }, {
            key: 'ui_locales',
            get: function get() {
                return this._ui_locales;
            }
        }, {
            key: 'acr_values',
            get: function get() {
                return this._acr_values;
            }
        }, {
            key: 'resource',
            get: function get() {
                return this._resource;
            }
        }, {
            key: 'response_mode',
            get: function get() {
                return this._response_mode;
            }
    
            // metadata
    
        }, {
            key: 'authority',
            get: function get() {
                return this._authority;
            },
            set: function set(value) {
                if (!this._authority) {
                    // one-time set only
                    this._authority = value;
                } else {
                    _Log.Log.error("OidcClientSettings.set_authority: authority has already been assigned.");
                    throw new Error("authority has already been assigned.");
                }
            }
        }, {
            key: 'metadataUrl',
            get: function get() {
                if (!this._metadataUrl) {
                    this._metadataUrl = this.authority;
    
                    if (this._metadataUrl && this._metadataUrl.indexOf(OidcMetadataUrlPath) < 0) {
                        if (this._metadataUrl[this._metadataUrl.length - 1] !== '/') {
                            this._metadataUrl += '/';
                        }
                        this._metadataUrl += OidcMetadataUrlPath;
                    }
                }
    
                return this._metadataUrl;
            }
    
            // settable/cachable metadata values
    
        }, {
            key: 'metadata',
            get: function get() {
                return this._metadata;
            },
            set: function set(value) {
                this._metadata = value;
            }
        }, {
            key: 'signingKeys',
            get: function get() {
                return this._signingKeys;
            },
            set: function set(value) {
                this._signingKeys = value;
            }
    
            // behavior flags
    
        }, {
            key: 'filterProtocolClaims',
            get: function get() {
                return this._filterProtocolClaims;
            }
        }, {
            key: 'loadUserInfo',
            get: function get() {
                return this._loadUserInfo;
            }
        }, {
            key: 'staleStateAge',
            get: function get() {
                return this._staleStateAge;
            }
        }, {
            key: 'clockSkew',
            get: function get() {
                return this._clockSkew;
            }
        }, {
            key: 'userInfoJwtIssuer',
            get: function get() {
                return this._userInfoJwtIssuer;
            }
        }, {
            key: 'stateStore',
            get: function get() {
                return this._stateStore;
            }
        }, {
            key: 'validator',
            get: function get() {
                return this._validator;
            }
        }, {
            key: 'metadataService',
            get: function get() {
                return this._metadataService;
            }
    
            // extra query params
    
        }, {
            key: 'extraQueryParams',
            get: function get() {
                return this._extraQueryParams;
            },
            set: function set(value) {
                if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') {
                    this._extraQueryParams = value;
                } else {
                    this._extraQueryParams = {};
                }
            }
    
            // extra token params
    
        }, {
            key: 'extraTokenParams',
            get: function get() {
                return this._extraTokenParams;
            },
            set: function set(value) {
                if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') {
                    this._extraTokenParams = value;
                } else {
                    this._extraTokenParams = {};
                }
            }
        }]);
    
        return OidcClientSettings;
    }();
    
    /***/ }),
    
    /***/ "./src/PopupNavigator.js":
    /*!*******************************!*\
      !*** ./src/PopupNavigator.js ***!
      \*******************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.PopupNavigator = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _PopupWindow = __webpack_require__(/*! ./PopupWindow.js */ "./src/PopupWindow.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var PopupNavigator = exports.PopupNavigator = function () {
        function PopupNavigator() {
            _classCallCheck(this, PopupNavigator);
        }
    
        PopupNavigator.prototype.prepare = function prepare(params) {
            var popup = new _PopupWindow.PopupWindow(params);
            return Promise.resolve(popup);
        };
    
        PopupNavigator.prototype.callback = function callback(url, keepOpen, delimiter) {
            _Log.Log.debug("PopupNavigator.callback");
    
            try {
                _PopupWindow.PopupWindow.notifyOpener(url, keepOpen, delimiter);
                return Promise.resolve();
            } catch (e) {
                return Promise.reject(e);
            }
        };
    
        return PopupNavigator;
    }();
    
    /***/ }),
    
    /***/ "./src/PopupWindow.js":
    /*!****************************!*\
      !*** ./src/PopupWindow.js ***!
      \****************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.PopupWindow = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _UrlUtility = __webpack_require__(/*! ./UrlUtility.js */ "./src/UrlUtility.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var CheckForPopupClosedInterval = 500;
    var DefaultPopupFeatures = 'location=no,toolbar=no,width=500,height=500,left=100,top=100;';
    //const DefaultPopupFeatures = 'location=no,toolbar=no,width=500,height=500,left=100,top=100;resizable=yes';
    
    var DefaultPopupTarget = "_blank";
    
    var PopupWindow = exports.PopupWindow = function () {
        function PopupWindow(params) {
            var _this = this;
    
            _classCallCheck(this, PopupWindow);
    
            this._promise = new Promise(function (resolve, reject) {
                _this._resolve = resolve;
                _this._reject = reject;
            });
    
            var target = params.popupWindowTarget || DefaultPopupTarget;
            var features = params.popupWindowFeatures || DefaultPopupFeatures;
    
            this._popup = window.open('', target, features);
            if (this._popup) {
                _Log.Log.debug("PopupWindow.ctor: popup successfully created");
                this._checkForPopupClosedTimer = window.setInterval(this._checkForPopupClosed.bind(this), CheckForPopupClosedInterval);
            }
        }
    
        PopupWindow.prototype.navigate = function navigate(params) {
            if (!this._popup) {
                this._error("PopupWindow.navigate: Error opening popup window");
            } else if (!params || !params.url) {
                this._error("PopupWindow.navigate: no url provided");
                this._error("No url provided");
            } else {
                _Log.Log.debug("PopupWindow.navigate: Setting URL in popup");
    
                this._id = params.id;
                if (this._id) {
                    window["popupCallback_" + params.id] = this._callback.bind(this);
                }
    
                this._popup.focus();
                this._popup.window.location = params.url;
            }
    
            return this.promise;
        };
    
        PopupWindow.prototype._success = function _success(data) {
            _Log.Log.debug("PopupWindow.callback: Successful response from popup window");
    
            this._cleanup();
            this._resolve(data);
        };
    
        PopupWindow.prototype._error = function _error(message) {
            _Log.Log.error("PopupWindow.error: ", message);
    
            this._cleanup();
            this._reject(new Error(message));
        };
    
        PopupWindow.prototype.close = function close() {
            this._cleanup(false);
        };
    
        PopupWindow.prototype._cleanup = function _cleanup(keepOpen) {
            _Log.Log.debug("PopupWindow.cleanup");
    
            window.clearInterval(this._checkForPopupClosedTimer);
            this._checkForPopupClosedTimer = null;
    
            delete window["popupCallback_" + this._id];
    
            if (this._popup && !keepOpen) {
                this._popup.close();
            }
            this._popup = null;
        };
    
        PopupWindow.prototype._checkForPopupClosed = function _checkForPopupClosed() {
            if (!this._popup || this._popup.closed) {
                this._error("Popup window closed");
            }
        };
    
        PopupWindow.prototype._callback = function _callback(url, keepOpen) {
            this._cleanup(keepOpen);
    
            if (url) {
                _Log.Log.debug("PopupWindow.callback success");
                this._success({ url: url });
            } else {
                _Log.Log.debug("PopupWindow.callback: Invalid response from popup");
                this._error("Invalid response from popup");
            }
        };
    
        PopupWindow.notifyOpener = function notifyOpener(url, keepOpen, delimiter) {
            if (window.opener) {
                url = url || window.location.href;
                if (url) {
                    var data = _UrlUtility.UrlUtility.parseUrlFragment(url, delimiter);
    
                    if (data.state) {
                        var name = "popupCallback_" + data.state;
                        var callback = window.opener[name];
                        if (callback) {
                            _Log.Log.debug("PopupWindow.notifyOpener: passing url message to opener");
                            callback(url, keepOpen);
                        } else {
                            _Log.Log.warn("PopupWindow.notifyOpener: no matching callback found on opener");
                        }
                    } else {
                        _Log.Log.warn("PopupWindow.notifyOpener: no state found in response url");
                    }
                }
            } else {
                _Log.Log.warn("PopupWindow.notifyOpener: no window.opener. Can't complete notification.");
            }
        };
    
        _createClass(PopupWindow, [{
            key: 'promise',
            get: function get() {
                return this._promise;
            }
        }]);
    
        return PopupWindow;
    }();
    
    /***/ }),
    
    /***/ "./src/RedirectNavigator.js":
    /*!**********************************!*\
      !*** ./src/RedirectNavigator.js ***!
      \**********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.RedirectNavigator = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var RedirectNavigator = exports.RedirectNavigator = function () {
        function RedirectNavigator() {
            _classCallCheck(this, RedirectNavigator);
        }
    
        RedirectNavigator.prototype.prepare = function prepare() {
            return Promise.resolve(this);
        };
    
        RedirectNavigator.prototype.navigate = function navigate(params) {
            if (!params || !params.url) {
                _Log.Log.error("RedirectNavigator.navigate: No url provided");
                return Promise.reject(new Error("No url provided"));
            }
    
            if (params.useReplaceToNavigate) {
                window.location.replace(params.url);
            } else {
                window.location = params.url;
            }
    
            return Promise.resolve();
        };
    
        _createClass(RedirectNavigator, [{
            key: "url",
            get: function get() {
                return window.location.href;
            }
        }]);
    
        return RedirectNavigator;
    }();
    
    /***/ }),
    
    /***/ "./src/ResponseValidator.js":
    /*!**********************************!*\
      !*** ./src/ResponseValidator.js ***!
      \**********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.ResponseValidator = undefined;
    
    var _typeof = typeof Symbol === "function" && typeof Symbol.iterator === "symbol" ? function (obj) { return typeof obj; } : function (obj) { return obj && typeof Symbol === "function" && obj.constructor === Symbol && obj !== Symbol.prototype ? "symbol" : typeof obj; };
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _MetadataService = __webpack_require__(/*! ./MetadataService.js */ "./src/MetadataService.js");
    
    var _UserInfoService = __webpack_require__(/*! ./UserInfoService.js */ "./src/UserInfoService.js");
    
    var _TokenClient = __webpack_require__(/*! ./TokenClient.js */ "./src/TokenClient.js");
    
    var _ErrorResponse = __webpack_require__(/*! ./ErrorResponse.js */ "./src/ErrorResponse.js");
    
    var _JoseUtil = __webpack_require__(/*! ./JoseUtil.js */ "./src/JoseUtil.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var ProtocolClaims = ["nonce", "at_hash", "iat", "nbf", "exp", "aud", "iss", "c_hash"];
    
    var ResponseValidator = exports.ResponseValidator = function () {
        function ResponseValidator(settings) {
            var MetadataServiceCtor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _MetadataService.MetadataService;
            var UserInfoServiceCtor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _UserInfoService.UserInfoService;
            var joseUtil = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _JoseUtil.JoseUtil;
            var TokenClientCtor = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : _TokenClient.TokenClient;
    
            _classCallCheck(this, ResponseValidator);
    
            if (!settings) {
                _Log.Log.error("ResponseValidator.ctor: No settings passed to ResponseValidator");
                throw new Error("settings");
            }
    
            this._settings = settings;
            this._metadataService = new MetadataServiceCtor(this._settings);
            this._userInfoService = new UserInfoServiceCtor(this._settings);
            this._joseUtil = joseUtil;
            this._tokenClient = new TokenClientCtor(this._settings);
        }
    
        ResponseValidator.prototype.validateSigninResponse = function validateSigninResponse(state, response) {
            var _this = this;
    
            _Log.Log.debug("ResponseValidator.validateSigninResponse");
    
            return this._processSigninParams(state, response).then(function (response) {
                _Log.Log.debug("ResponseValidator.validateSigninResponse: state processed");
                return _this._validateTokens(state, response).then(function (response) {
                    _Log.Log.debug("ResponseValidator.validateSigninResponse: tokens validated");
                    return _this._processClaims(state, response).then(function (response) {
                        _Log.Log.debug("ResponseValidator.validateSigninResponse: claims processed");
                        return response;
                    });
                });
            });
        };
    
        ResponseValidator.prototype.validateSignoutResponse = function validateSignoutResponse(state, response) {
            if (state.id !== response.state) {
                _Log.Log.error("ResponseValidator.validateSignoutResponse: State does not match");
                return Promise.reject(new Error("State does not match"));
            }
    
            // now that we know the state matches, take the stored data
            // and set it into the response so callers can get their state
            // this is important for both success & error outcomes
            _Log.Log.debug("ResponseValidator.validateSignoutResponse: state validated");
            response.state = state.data;
    
            if (response.error) {
                _Log.Log.warn("ResponseValidator.validateSignoutResponse: Response was error", response.error);
                return Promise.reject(new _ErrorResponse.ErrorResponse(response));
            }
    
            return Promise.resolve(response);
        };
    
        ResponseValidator.prototype._processSigninParams = function _processSigninParams(state, response) {
            if (state.id !== response.state) {
                _Log.Log.error("ResponseValidator._processSigninParams: State does not match");
                return Promise.reject(new Error("State does not match"));
            }
    
            if (!state.client_id) {
                _Log.Log.error("ResponseValidator._processSigninParams: No client_id on state");
                return Promise.reject(new Error("No client_id on state"));
            }
    
            if (!state.authority) {
                _Log.Log.error("ResponseValidator._processSigninParams: No authority on state");
                return Promise.reject(new Error("No authority on state"));
            }
    
            // this allows the authority to be loaded from the signin state
            if (!this._settings.authority) {
                this._settings.authority = state.authority;
            }
            // ensure we're using the correct authority if the authority is not loaded from signin state
            else if (this._settings.authority && this._settings.authority !== state.authority) {
                    _Log.Log.error("ResponseValidator._processSigninParams: authority mismatch on settings vs. signin state");
                    return Promise.reject(new Error("authority mismatch on settings vs. signin state"));
                }
            // this allows the client_id to be loaded from the signin state
            if (!this._settings.client_id) {
                this._settings.client_id = state.client_id;
            }
            // ensure we're using the correct client_id if the client_id is not loaded from signin state
            else if (this._settings.client_id && this._settings.client_id !== state.client_id) {
                    _Log.Log.error("ResponseValidator._processSigninParams: client_id mismatch on settings vs. signin state");
                    return Promise.reject(new Error("client_id mismatch on settings vs. signin state"));
                }
    
            // now that we know the state matches, take the stored data
            // and set it into the response so callers can get their state
            // this is important for both success & error outcomes
            _Log.Log.debug("ResponseValidator._processSigninParams: state validated");
            response.state = state.data;
    
            if (response.error) {
                _Log.Log.warn("ResponseValidator._processSigninParams: Response was error", response.error);
                return Promise.reject(new _ErrorResponse.ErrorResponse(response));
            }
    
            if (state.nonce && !response.id_token) {
                _Log.Log.error("ResponseValidator._processSigninParams: Expecting id_token in response");
                return Promise.reject(new Error("No id_token in response"));
            }
    
            if (!state.nonce && response.id_token) {
                _Log.Log.error("ResponseValidator._processSigninParams: Not expecting id_token in response");
                return Promise.reject(new Error("Unexpected id_token in response"));
            }
    
            if (state.code_verifier && !response.code) {
                _Log.Log.error("ResponseValidator._processSigninParams: Expecting code in response");
                return Promise.reject(new Error("No code in response"));
            }
    
            if (!state.code_verifier && response.code) {
                _Log.Log.error("ResponseValidator._processSigninParams: Not expecting code in response");
                return Promise.reject(new Error("Unexpected code in response"));
            }
    
            if (!response.scope) {
                // if there's no scope on the response, then assume all scopes granted (per-spec) and copy over scopes from original request
                response.scope = state.scope;
            }
    
            return Promise.resolve(response);
        };
    
        ResponseValidator.prototype._processClaims = function _processClaims(state, response) {
            var _this2 = this;
    
            if (response.isOpenIdConnect) {
                _Log.Log.debug("ResponseValidator._processClaims: response is OIDC, processing claims");
    
                response.profile = this._filterProtocolClaims(response.profile);
    
                if (state.skipUserInfo !== true && this._settings.loadUserInfo && response.access_token) {
                    _Log.Log.debug("ResponseValidator._processClaims: loading user info");
    
                    return this._userInfoService.getClaims(response.access_token).then(function (claims) {
                        _Log.Log.debug("ResponseValidator._processClaims: user info claims received from user info endpoint");
    
                        if (claims.sub !== response.profile.sub) {
                            _Log.Log.error("ResponseValidator._processClaims: sub from user info endpoint does not match sub in access_token");
                            return Promise.reject(new Error("sub from user info endpoint does not match sub in access_token"));
                        }
    
                        response.profile = _this2._mergeClaims(response.profile, claims);
                        _Log.Log.debug("ResponseValidator._processClaims: user info claims received, updated profile:", response.profile);
    
                        return response;
                    });
                } else {
                    _Log.Log.debug("ResponseValidator._processClaims: not loading user info");
                }
            } else {
                _Log.Log.debug("ResponseValidator._processClaims: response is not OIDC, not processing claims");
            }
    
            return Promise.resolve(response);
        };
    
        ResponseValidator.prototype._mergeClaims = function _mergeClaims(claims1, claims2) {
            var result = Object.assign({}, claims1);
    
            for (var name in claims2) {
                var values = claims2[name];
                if (!Array.isArray(values)) {
                    values = [values];
                }
    
                for (var i = 0; i < values.length; i++) {
                    var value = values[i];
                    if (!result[name]) {
                        result[name] = value;
                    } else if (Array.isArray(result[name])) {
                        if (result[name].indexOf(value) < 0) {
                            result[name].push(value);
                        }
                    } else if (result[name] !== value) {
                        if ((typeof value === 'undefined' ? 'undefined' : _typeof(value)) === 'object') {
                            result[name] = this._mergeClaims(result[name], value);
                        } else {
                            result[name] = [result[name], value];
                        }
                    }
                }
            }
    
            return result;
        };
    
        ResponseValidator.prototype._filterProtocolClaims = function _filterProtocolClaims(claims) {
            _Log.Log.debug("ResponseValidator._filterProtocolClaims, incoming claims:", claims);
    
            var result = Object.assign({}, claims);
    
            if (this._settings._filterProtocolClaims) {
                ProtocolClaims.forEach(function (type) {
                    delete result[type];
                });
    
                _Log.Log.debug("ResponseValidator._filterProtocolClaims: protocol claims filtered", result);
            } else {
                _Log.Log.debug("ResponseValidator._filterProtocolClaims: protocol claims not filtered");
            }
    
            return result;
        };
    
        ResponseValidator.prototype._validateTokens = function _validateTokens(state, response) {
            if (response.code) {
                _Log.Log.debug("ResponseValidator._validateTokens: Validating code");
                return this._processCode(state, response);
            }
    
            if (response.id_token) {
                if (response.access_token) {
                    _Log.Log.debug("ResponseValidator._validateTokens: Validating id_token and access_token");
                    return this._validateIdTokenAndAccessToken(state, response);
                }
    
                _Log.Log.debug("ResponseValidator._validateTokens: Validating id_token");
                return this._validateIdToken(state, response);
            }
    
            _Log.Log.debug("ResponseValidator._validateTokens: No code to process or id_token to validate");
            return Promise.resolve(response);
        };
    
        ResponseValidator.prototype._processCode = function _processCode(state, response) {
            var _this3 = this;
    
            var request = {
                client_id: state.client_id,
                client_secret: state.client_secret,
                code: response.code,
                redirect_uri: state.redirect_uri,
                code_verifier: state.code_verifier
            };
    
            if (state.extraTokenParams && _typeof(state.extraTokenParams) === 'object') {
                Object.assign(request, state.extraTokenParams);
            }
    
            return this._tokenClient.exchangeCode(request).then(function (tokenResponse) {
    
                for (var key in tokenResponse) {
                    response[key] = tokenResponse[key];
                }
    
                if (response.id_token) {
                    _Log.Log.debug("ResponseValidator._processCode: token response successful, processing id_token");
                    return _this3._validateIdTokenAttributes(state, response);
                } else {
                    _Log.Log.debug("ResponseValidator._processCode: token response successful, returning response");
                }
    
                return response;
            });
        };
    
        ResponseValidator.prototype._validateIdTokenAttributes = function _validateIdTokenAttributes(state, response) {
            var _this4 = this;
    
            return this._metadataService.getIssuer().then(function (issuer) {
    
                var audience = state.client_id;
                var clockSkewInSeconds = _this4._settings.clockSkew;
                _Log.Log.debug("ResponseValidator._validateIdTokenAttributes: Validaing JWT attributes; using clock skew (in seconds) of: ", clockSkewInSeconds);
    
                return _this4._joseUtil.validateJwtAttributes(response.id_token, issuer, audience, clockSkewInSeconds).then(function (payload) {
    
                    if (state.nonce && state.nonce !== payload.nonce) {
                        _Log.Log.error("ResponseValidator._validateIdTokenAttributes: Invalid nonce in id_token");
                        return Promise.reject(new Error("Invalid nonce in id_token"));
                    }
    
                    if (!payload.sub) {
                        _Log.Log.error("ResponseValidator._validateIdTokenAttributes: No sub present in id_token");
                        return Promise.reject(new Error("No sub present in id_token"));
                    }
    
                    response.profile = payload;
                    return response;
                });
            });
        };
    
        ResponseValidator.prototype._validateIdTokenAndAccessToken = function _validateIdTokenAndAccessToken(state, response) {
            var _this5 = this;
    
            return this._validateIdToken(state, response).then(function (response) {
                return _this5._validateAccessToken(response);
            });
        };
    
        ResponseValidator.prototype._validateIdToken = function _validateIdToken(state, response) {
            var _this6 = this;
    
            if (!state.nonce) {
                _Log.Log.error("ResponseValidator._validateIdToken: No nonce on state");
                return Promise.reject(new Error("No nonce on state"));
            }
    
            var jwt = this._joseUtil.parseJwt(response.id_token);
            if (!jwt || !jwt.header || !jwt.payload) {
                _Log.Log.error("ResponseValidator._validateIdToken: Failed to parse id_token", jwt);
                return Promise.reject(new Error("Failed to parse id_token"));
            }
    
            if (state.nonce !== jwt.payload.nonce) {
                _Log.Log.error("ResponseValidator._validateIdToken: Invalid nonce in id_token");
                return Promise.reject(new Error("Invalid nonce in id_token"));
            }
    
            var kid = jwt.header.kid;
    
            return this._metadataService.getIssuer().then(function (issuer) {
                _Log.Log.debug("ResponseValidator._validateIdToken: Received issuer");
    
                return _this6._metadataService.getSigningKeys().then(function (keys) {
                    if (!keys) {
                        _Log.Log.error("ResponseValidator._validateIdToken: No signing keys from metadata");
                        return Promise.reject(new Error("No signing keys from metadata"));
                    }
    
                    _Log.Log.debug("ResponseValidator._validateIdToken: Received signing keys");
                    var key = void 0;
                    if (!kid) {
                        keys = _this6._filterByAlg(keys, jwt.header.alg);
    
                        if (keys.length > 1) {
                            _Log.Log.error("ResponseValidator._validateIdToken: No kid found in id_token and more than one key found in metadata");
                            return Promise.reject(new Error("No kid found in id_token and more than one key found in metadata"));
                        } else {
                            // kid is mandatory only when there are multiple keys in the referenced JWK Set document
                            // see http://openid.net/specs/openid-connect-core-1_0.html#Signing
                            key = keys[0];
                        }
                    } else {
                        key = keys.filter(function (key) {
                            return key.kid === kid;
                        })[0];
                    }
    
                    if (!key) {
                        _Log.Log.error("ResponseValidator._validateIdToken: No key matching kid or alg found in signing keys");
                        return Promise.reject(new Error("No key matching kid or alg found in signing keys"));
                    }
    
                    var audience = state.client_id;
    
                    var clockSkewInSeconds = _this6._settings.clockSkew;
                    _Log.Log.debug("ResponseValidator._validateIdToken: Validaing JWT; using clock skew (in seconds) of: ", clockSkewInSeconds);
    
                    return _this6._joseUtil.validateJwt(response.id_token, key, issuer, audience, clockSkewInSeconds).then(function () {
                        _Log.Log.debug("ResponseValidator._validateIdToken: JWT validation successful");
    
                        if (!jwt.payload.sub) {
                            _Log.Log.error("ResponseValidator._validateIdToken: No sub present in id_token");
                            return Promise.reject(new Error("No sub present in id_token"));
                        }
    
                        response.profile = jwt.payload;
    
                        return response;
                    });
                });
            });
        };
    
        ResponseValidator.prototype._filterByAlg = function _filterByAlg(keys, alg) {
            var kty = null;
            if (alg.startsWith("RS")) {
                kty = "RSA";
            } else if (alg.startsWith("PS")) {
                kty = "PS";
            } else if (alg.startsWith("ES")) {
                kty = "EC";
            } else {
                _Log.Log.debug("ResponseValidator._filterByAlg: alg not supported: ", alg);
                return [];
            }
    
            _Log.Log.debug("ResponseValidator._filterByAlg: Looking for keys that match kty: ", kty);
    
            keys = keys.filter(function (key) {
                return key.kty === kty;
            });
    
            _Log.Log.debug("ResponseValidator._filterByAlg: Number of keys that match kty: ", kty, keys.length);
    
            return keys;
        };
    
        ResponseValidator.prototype._validateAccessToken = function _validateAccessToken(response) {
            if (!response.profile) {
                _Log.Log.error("ResponseValidator._validateAccessToken: No profile loaded from id_token");
                return Promise.reject(new Error("No profile loaded from id_token"));
            }
    
            if (!response.profile.at_hash) {
                _Log.Log.error("ResponseValidator._validateAccessToken: No at_hash in id_token");
                return Promise.reject(new Error("No at_hash in id_token"));
            }
    
            if (!response.id_token) {
                _Log.Log.error("ResponseValidator._validateAccessToken: No id_token");
                return Promise.reject(new Error("No id_token"));
            }
    
            var jwt = this._joseUtil.parseJwt(response.id_token);
            if (!jwt || !jwt.header) {
                _Log.Log.error("ResponseValidator._validateAccessToken: Failed to parse id_token", jwt);
                return Promise.reject(new Error("Failed to parse id_token"));
            }
    
            var hashAlg = jwt.header.alg;
            if (!hashAlg || hashAlg.length !== 5) {
                _Log.Log.error("ResponseValidator._validateAccessToken: Unsupported alg:", hashAlg);
                return Promise.reject(new Error("Unsupported alg: " + hashAlg));
            }
    
            var hashBits = hashAlg.substr(2, 3);
            if (!hashBits) {
                _Log.Log.error("ResponseValidator._validateAccessToken: Unsupported alg:", hashAlg, hashBits);
                return Promise.reject(new Error("Unsupported alg: " + hashAlg));
            }
    
            hashBits = parseInt(hashBits);
            if (hashBits !== 256 && hashBits !== 384 && hashBits !== 512) {
                _Log.Log.error("ResponseValidator._validateAccessToken: Unsupported alg:", hashAlg, hashBits);
                return Promise.reject(new Error("Unsupported alg: " + hashAlg));
            }
    
            var sha = "sha" + hashBits;
            var hash = this._joseUtil.hashString(response.access_token, sha);
            if (!hash) {
                _Log.Log.error("ResponseValidator._validateAccessToken: access_token hash failed:", sha);
                return Promise.reject(new Error("Failed to validate at_hash"));
            }
    
            var left = hash.substr(0, hash.length / 2);
            var left_b64u = this._joseUtil.hexToBase64Url(left);
            if (left_b64u !== response.profile.at_hash) {
                _Log.Log.error("ResponseValidator._validateAccessToken: Failed to validate at_hash", left_b64u, response.profile.at_hash);
                return Promise.reject(new Error("Failed to validate at_hash"));
            }
    
            _Log.Log.debug("ResponseValidator._validateAccessToken: success");
    
            return Promise.resolve(response);
        };
    
        return ResponseValidator;
    }();
    
    /***/ }),
    
    /***/ "./src/SessionMonitor.js":
    /*!*******************************!*\
      !*** ./src/SessionMonitor.js ***!
      \*******************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.SessionMonitor = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _CheckSessionIFrame = __webpack_require__(/*! ./CheckSessionIFrame.js */ "./src/CheckSessionIFrame.js");
    
    var _Global = __webpack_require__(/*! ./Global.js */ "./src/Global.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var SessionMonitor = exports.SessionMonitor = function () {
        function SessionMonitor(userManager) {
            var _this = this;
    
            var CheckSessionIFrameCtor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _CheckSessionIFrame.CheckSessionIFrame;
            var timer = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _Global.Global.timer;
    
            _classCallCheck(this, SessionMonitor);
    
            if (!userManager) {
                _Log.Log.error("SessionMonitor.ctor: No user manager passed to SessionMonitor");
                throw new Error("userManager");
            }
    
            this._userManager = userManager;
            this._CheckSessionIFrameCtor = CheckSessionIFrameCtor;
            this._timer = timer;
    
            this._userManager.events.addUserLoaded(this._start.bind(this));
            this._userManager.events.addUserUnloaded(this._stop.bind(this));
    
            this._userManager.getUser().then(function (user) {
                // doing this manually here since calling getUser 
                // doesn't trigger load event.
                if (user) {
                    _this._start(user);
                } else if (_this._settings.monitorAnonymousSession) {
                    _this._userManager.querySessionStatus().then(function (session) {
                        var tmpUser = {
                            session_state: session.session_state
                        };
                        if (session.sub && session.sid) {
                            tmpUser.profile = {
                                sub: session.sub,
                                sid: session.sid
                            };
                        }
                        _this._start(tmpUser);
                    }).catch(function (err) {
                        // catch to suppress errors since we're in a ctor
                        _Log.Log.error("SessionMonitor ctor: error from querySessionStatus:", err.message);
                    });
                }
            }).catch(function (err) {
                // catch to suppress errors since we're in a ctor
                _Log.Log.error("SessionMonitor ctor: error from getUser:", err.message);
            });
        }
    
        SessionMonitor.prototype._start = function _start(user) {
            var _this2 = this;
    
            var session_state = user.session_state;
    
            if (session_state) {
                if (user.profile) {
                    this._sub = user.profile.sub;
                    this._sid = user.profile.sid;
                    _Log.Log.debug("SessionMonitor._start: session_state:", session_state, ", sub:", this._sub);
                } else {
                    this._sub = undefined;
                    this._sid = undefined;
                    _Log.Log.debug("SessionMonitor._start: session_state:", session_state, ", anonymous user");
                }
    
                if (!this._checkSessionIFrame) {
                    this._metadataService.getCheckSessionIframe().then(function (url) {
                        if (url) {
                            _Log.Log.debug("SessionMonitor._start: Initializing check session iframe");
    
                            var client_id = _this2._client_id;
                            var interval = _this2._checkSessionInterval;
                            var stopOnError = _this2._stopCheckSessionOnError;
    
                            _this2._checkSessionIFrame = new _this2._CheckSessionIFrameCtor(_this2._callback.bind(_this2), client_id, url, interval, stopOnError);
                            _this2._checkSessionIFrame.load().then(function () {
                                _this2._checkSessionIFrame.start(session_state);
                            });
                        } else {
                            _Log.Log.warn("SessionMonitor._start: No check session iframe found in the metadata");
                        }
                    }).catch(function (err) {
                        // catch to suppress errors since we're in non-promise callback
                        _Log.Log.error("SessionMonitor._start: Error from getCheckSessionIframe:", err.message);
                    });
                } else {
                    this._checkSessionIFrame.start(session_state);
                }
            }
        };
    
        SessionMonitor.prototype._stop = function _stop() {
            var _this3 = this;
    
            this._sub = undefined;
            this._sid = undefined;
    
            if (this._checkSessionIFrame) {
                _Log.Log.debug("SessionMonitor._stop");
                this._checkSessionIFrame.stop();
            }
    
            if (this._settings.monitorAnonymousSession) {
                // using a timer to delay re-initialization to avoid race conditions during signout
                var timerHandle = this._timer.setInterval(function () {
                    _this3._timer.clearInterval(timerHandle);
    
                    _this3._userManager.querySessionStatus().then(function (session) {
                        var tmpUser = {
                            session_state: session.session_state
                        };
                        if (session.sub && session.sid) {
                            tmpUser.profile = {
                                sub: session.sub,
                                sid: session.sid
                            };
                        }
                        _this3._start(tmpUser);
                    }).catch(function (err) {
                        // catch to suppress errors since we're in a callback
                        _Log.Log.error("SessionMonitor: error from querySessionStatus:", err.message);
                    });
                }, 1000);
            }
        };
    
        SessionMonitor.prototype._callback = function _callback() {
            var _this4 = this;
    
            this._userManager.querySessionStatus().then(function (session) {
                var raiseEvent = true;
    
                if (session) {
                    if (session.sub === _this4._sub) {
                        raiseEvent = false;
                        _this4._checkSessionIFrame.start(session.session_state);
    
                        if (session.sid === _this4._sid) {
                            _Log.Log.debug("SessionMonitor._callback: Same sub still logged in at OP, restarting check session iframe; session_state:", session.session_state);
                        } else {
                            _Log.Log.debug("SessionMonitor._callback: Same sub still logged in at OP, session state has changed, restarting check session iframe; session_state:", session.session_state);
                            _this4._userManager.events._raiseUserSessionChanged();
                        }
                    } else {
                        _Log.Log.debug("SessionMonitor._callback: Different subject signed into OP:", session.sub);
                    }
                } else {
                    _Log.Log.debug("SessionMonitor._callback: Subject no longer signed into OP");
                }
    
                if (raiseEvent) {
                    if (_this4._sub) {
                        _Log.Log.debug("SessionMonitor._callback: SessionMonitor._callback; raising signed out event");
                        _this4._userManager.events._raiseUserSignedOut();
                    } else {
                        _Log.Log.debug("SessionMonitor._callback: SessionMonitor._callback; raising signed in event");
                        _this4._userManager.events._raiseUserSignedIn();
                    }
                }
            }).catch(function (err) {
                if (_this4._sub) {
                    _Log.Log.debug("SessionMonitor._callback: Error calling queryCurrentSigninSession; raising signed out event", err.message);
                    _this4._userManager.events._raiseUserSignedOut();
                }
            });
        };
    
        _createClass(SessionMonitor, [{
            key: '_settings',
            get: function get() {
                return this._userManager.settings;
            }
        }, {
            key: '_metadataService',
            get: function get() {
                return this._userManager.metadataService;
            }
        }, {
            key: '_client_id',
            get: function get() {
                return this._settings.client_id;
            }
        }, {
            key: '_checkSessionInterval',
            get: function get() {
                return this._settings.checkSessionInterval;
            }
        }, {
            key: '_stopCheckSessionOnError',
            get: function get() {
                return this._settings.stopCheckSessionOnError;
            }
        }]);
    
        return SessionMonitor;
    }();
    
    /***/ }),
    
    /***/ "./src/SigninRequest.js":
    /*!******************************!*\
      !*** ./src/SigninRequest.js ***!
      \******************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.SigninRequest = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _UrlUtility = __webpack_require__(/*! ./UrlUtility.js */ "./src/UrlUtility.js");
    
    var _SigninState = __webpack_require__(/*! ./SigninState.js */ "./src/SigninState.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var SigninRequest = exports.SigninRequest = function () {
        function SigninRequest(_ref) {
            var url = _ref.url,
                client_id = _ref.client_id,
                redirect_uri = _ref.redirect_uri,
                response_type = _ref.response_type,
                scope = _ref.scope,
                authority = _ref.authority,
                data = _ref.data,
                prompt = _ref.prompt,
                display = _ref.display,
                max_age = _ref.max_age,
                ui_locales = _ref.ui_locales,
                id_token_hint = _ref.id_token_hint,
                login_hint = _ref.login_hint,
                acr_values = _ref.acr_values,
                resource = _ref.resource,
                response_mode = _ref.response_mode,
                request = _ref.request,
                request_uri = _ref.request_uri,
                extraQueryParams = _ref.extraQueryParams,
                request_type = _ref.request_type,
                client_secret = _ref.client_secret,
                extraTokenParams = _ref.extraTokenParams,
                skipUserInfo = _ref.skipUserInfo;
    
            _classCallCheck(this, SigninRequest);
    
            if (!url) {
                _Log.Log.error("SigninRequest.ctor: No url passed");
                throw new Error("url");
            }
            if (!client_id) {
                _Log.Log.error("SigninRequest.ctor: No client_id passed");
                throw new Error("client_id");
            }
            if (!redirect_uri) {
                _Log.Log.error("SigninRequest.ctor: No redirect_uri passed");
                throw new Error("redirect_uri");
            }
            if (!response_type) {
                _Log.Log.error("SigninRequest.ctor: No response_type passed");
                throw new Error("response_type");
            }
            if (!scope) {
                _Log.Log.error("SigninRequest.ctor: No scope passed");
                throw new Error("scope");
            }
            if (!authority) {
                _Log.Log.error("SigninRequest.ctor: No authority passed");
                throw new Error("authority");
            }
    
            var oidc = SigninRequest.isOidc(response_type);
            var code = SigninRequest.isCode(response_type);
    
            if (!response_mode) {
                response_mode = SigninRequest.isCode(response_type) ? "query" : null;
            }
    
            this.state = new _SigninState.SigninState({ nonce: oidc,
                data: data, client_id: client_id, authority: authority, redirect_uri: redirect_uri,
                code_verifier: code,
                request_type: request_type, response_mode: response_mode,
                client_secret: client_secret, scope: scope, extraTokenParams: extraTokenParams, skipUserInfo: skipUserInfo });
    
            url = _UrlUtility.UrlUtility.addQueryParam(url, "client_id", client_id);
            url = _UrlUtility.UrlUtility.addQueryParam(url, "redirect_uri", redirect_uri);
            url = _UrlUtility.UrlUtility.addQueryParam(url, "response_type", response_type);
            url = _UrlUtility.UrlUtility.addQueryParam(url, "scope", scope);
    
            url = _UrlUtility.UrlUtility.addQueryParam(url, "state", this.state.id);
            if (oidc) {
                url = _UrlUtility.UrlUtility.addQueryParam(url, "nonce", this.state.nonce);
            }
            if (code) {
                url = _UrlUtility.UrlUtility.addQueryParam(url, "code_challenge", this.state.code_challenge);
                url = _UrlUtility.UrlUtility.addQueryParam(url, "code_challenge_method", "S256");
            }
    
            var optional = { prompt: prompt, display: display, max_age: max_age, ui_locales: ui_locales, id_token_hint: id_token_hint, login_hint: login_hint, acr_values: acr_values, resource: resource, request: request, request_uri: request_uri, response_mode: response_mode };
            for (var key in optional) {
                if (optional[key]) {
                    url = _UrlUtility.UrlUtility.addQueryParam(url, key, optional[key]);
                }
            }
    
            for (var _key in extraQueryParams) {
                url = _UrlUtility.UrlUtility.addQueryParam(url, _key, extraQueryParams[_key]);
            }
    
            this.url = url;
        }
    
        SigninRequest.isOidc = function isOidc(response_type) {
            var result = response_type.split(/\s+/g).filter(function (item) {
                return item === "id_token";
            });
            return !!result[0];
        };
    
        SigninRequest.isOAuth = function isOAuth(response_type) {
            var result = response_type.split(/\s+/g).filter(function (item) {
                return item === "token";
            });
            return !!result[0];
        };
    
        SigninRequest.isCode = function isCode(response_type) {
            var result = response_type.split(/\s+/g).filter(function (item) {
                return item === "code";
            });
            return !!result[0];
        };
    
        return SigninRequest;
    }();
    
    /***/ }),
    
    /***/ "./src/SigninResponse.js":
    /*!*******************************!*\
      !*** ./src/SigninResponse.js ***!
      \*******************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.SigninResponse = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _UrlUtility = __webpack_require__(/*! ./UrlUtility.js */ "./src/UrlUtility.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var OidcScope = "openid";
    
    var SigninResponse = exports.SigninResponse = function () {
        function SigninResponse(url) {
            var delimiter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "#";
    
            _classCallCheck(this, SigninResponse);
    
            var values = _UrlUtility.UrlUtility.parseUrlFragment(url, delimiter);
    
            this.error = values.error;
            this.error_description = values.error_description;
            this.error_uri = values.error_uri;
    
            this.code = values.code;
            this.state = values.state;
            this.id_token = values.id_token;
            this.session_state = values.session_state;
            this.access_token = values.access_token;
            this.token_type = values.token_type;
            this.scope = values.scope;
            this.profile = undefined; // will be set from ResponseValidator
    
            this.expires_in = values.expires_in;
        }
    
        _createClass(SigninResponse, [{
            key: "expires_in",
            get: function get() {
                if (this.expires_at) {
                    var now = parseInt(Date.now() / 1000);
                    return this.expires_at - now;
                }
                return undefined;
            },
            set: function set(value) {
                var expires_in = parseInt(value);
                if (typeof expires_in === 'number' && expires_in > 0) {
                    var now = parseInt(Date.now() / 1000);
                    this.expires_at = now + expires_in;
                }
            }
        }, {
            key: "expired",
            get: function get() {
                var expires_in = this.expires_in;
                if (expires_in !== undefined) {
                    return expires_in <= 0;
                }
                return undefined;
            }
        }, {
            key: "scopes",
            get: function get() {
                return (this.scope || "").split(" ");
            }
        }, {
            key: "isOpenIdConnect",
            get: function get() {
                return this.scopes.indexOf(OidcScope) >= 0 || !!this.id_token;
            }
        }]);
    
        return SigninResponse;
    }();
    
    /***/ }),
    
    /***/ "./src/SigninState.js":
    /*!****************************!*\
      !*** ./src/SigninState.js ***!
      \****************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.SigninState = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _State2 = __webpack_require__(/*! ./State.js */ "./src/State.js");
    
    var _JoseUtil = __webpack_require__(/*! ./JoseUtil.js */ "./src/JoseUtil.js");
    
    var _random = __webpack_require__(/*! ./random.js */ "./src/random.js");
    
    var _random2 = _interopRequireDefault(_random);
    
    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
    
    function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var SigninState = exports.SigninState = function (_State) {
        _inherits(SigninState, _State);
    
        function SigninState() {
            var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                nonce = _ref.nonce,
                authority = _ref.authority,
                client_id = _ref.client_id,
                redirect_uri = _ref.redirect_uri,
                code_verifier = _ref.code_verifier,
                response_mode = _ref.response_mode,
                client_secret = _ref.client_secret,
                scope = _ref.scope,
                extraTokenParams = _ref.extraTokenParams,
                skipUserInfo = _ref.skipUserInfo;
    
            _classCallCheck(this, SigninState);
    
            var _this = _possibleConstructorReturn(this, _State.call(this, arguments[0]));
    
            if (nonce === true) {
                _this._nonce = (0, _random2.default)();
            } else if (nonce) {
                _this._nonce = nonce;
            }
    
            if (code_verifier === true) {
                // random() produces 32 length
                _this._code_verifier = (0, _random2.default)() + (0, _random2.default)() + (0, _random2.default)();
            } else if (code_verifier) {
                _this._code_verifier = code_verifier;
            }
    
            if (_this.code_verifier) {
                var hash = _JoseUtil.JoseUtil.hashString(_this.code_verifier, "SHA256");
                _this._code_challenge = _JoseUtil.JoseUtil.hexToBase64Url(hash);
            }
    
            _this._redirect_uri = redirect_uri;
            _this._authority = authority;
            _this._client_id = client_id;
            _this._response_mode = response_mode;
            _this._client_secret = client_secret;
            _this._scope = scope;
            _this._extraTokenParams = extraTokenParams;
            _this._skipUserInfo = skipUserInfo;
            return _this;
        }
    
        SigninState.prototype.toStorageString = function toStorageString() {
            _Log.Log.debug("SigninState.toStorageString");
            return JSON.stringify({
                id: this.id,
                data: this.data,
                created: this.created,
                request_type: this.request_type,
                nonce: this.nonce,
                code_verifier: this.code_verifier,
                redirect_uri: this.redirect_uri,
                authority: this.authority,
                client_id: this.client_id,
                response_mode: this.response_mode,
                client_secret: this.client_secret,
                scope: this.scope,
                extraTokenParams: this.extraTokenParams,
                skipUserInfo: this.skipUserInfo
            });
        };
    
        SigninState.fromStorageString = function fromStorageString(storageString) {
            _Log.Log.debug("SigninState.fromStorageString");
            var data = JSON.parse(storageString);
            return new SigninState(data);
        };
    
        _createClass(SigninState, [{
            key: 'nonce',
            get: function get() {
                return this._nonce;
            }
        }, {
            key: 'authority',
            get: function get() {
                return this._authority;
            }
        }, {
            key: 'client_id',
            get: function get() {
                return this._client_id;
            }
        }, {
            key: 'redirect_uri',
            get: function get() {
                return this._redirect_uri;
            }
        }, {
            key: 'code_verifier',
            get: function get() {
                return this._code_verifier;
            }
        }, {
            key: 'code_challenge',
            get: function get() {
                return this._code_challenge;
            }
        }, {
            key: 'response_mode',
            get: function get() {
                return this._response_mode;
            }
        }, {
            key: 'client_secret',
            get: function get() {
                return this._client_secret;
            }
        }, {
            key: 'scope',
            get: function get() {
                return this._scope;
            }
        }, {
            key: 'extraTokenParams',
            get: function get() {
                return this._extraTokenParams;
            }
        }, {
            key: 'skipUserInfo',
            get: function get() {
                return this._skipUserInfo;
            }
        }]);
    
        return SigninState;
    }(_State2.State);
    
    /***/ }),
    
    /***/ "./src/SignoutRequest.js":
    /*!*******************************!*\
      !*** ./src/SignoutRequest.js ***!
      \*******************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.SignoutRequest = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _UrlUtility = __webpack_require__(/*! ./UrlUtility.js */ "./src/UrlUtility.js");
    
    var _State = __webpack_require__(/*! ./State.js */ "./src/State.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var SignoutRequest = exports.SignoutRequest = function SignoutRequest(_ref) {
        var url = _ref.url,
            id_token_hint = _ref.id_token_hint,
            post_logout_redirect_uri = _ref.post_logout_redirect_uri,
            data = _ref.data,
            extraQueryParams = _ref.extraQueryParams,
            request_type = _ref.request_type;
    
        _classCallCheck(this, SignoutRequest);
    
        if (!url) {
            _Log.Log.error("SignoutRequest.ctor: No url passed");
            throw new Error("url");
        }
    
        if (id_token_hint) {
            url = _UrlUtility.UrlUtility.addQueryParam(url, "id_token_hint", id_token_hint);
        }
    
        if (post_logout_redirect_uri) {
            url = _UrlUtility.UrlUtility.addQueryParam(url, "post_logout_redirect_uri", post_logout_redirect_uri);
    
            if (data) {
                this.state = new _State.State({ data: data, request_type: request_type });
    
                url = _UrlUtility.UrlUtility.addQueryParam(url, "state", this.state.id);
            }
        }
    
        for (var key in extraQueryParams) {
            url = _UrlUtility.UrlUtility.addQueryParam(url, key, extraQueryParams[key]);
        }
    
        this.url = url;
    };
    
    /***/ }),
    
    /***/ "./src/SignoutResponse.js":
    /*!********************************!*\
      !*** ./src/SignoutResponse.js ***!
      \********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
            value: true
    });
    exports.SignoutResponse = undefined;
    
    var _UrlUtility = __webpack_require__(/*! ./UrlUtility.js */ "./src/UrlUtility.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var SignoutResponse = exports.SignoutResponse = function SignoutResponse(url) {
            _classCallCheck(this, SignoutResponse);
    
            var values = _UrlUtility.UrlUtility.parseUrlFragment(url, "?");
    
            this.error = values.error;
            this.error_description = values.error_description;
            this.error_uri = values.error_uri;
    
            this.state = values.state;
    };
    
    /***/ }),
    
    /***/ "./src/SilentRenewService.js":
    /*!***********************************!*\
      !*** ./src/SilentRenewService.js ***!
      \***********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.SilentRenewService = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var SilentRenewService = exports.SilentRenewService = function () {
        function SilentRenewService(userManager) {
            _classCallCheck(this, SilentRenewService);
    
            this._userManager = userManager;
        }
    
        SilentRenewService.prototype.start = function start() {
            if (!this._callback) {
                this._callback = this._tokenExpiring.bind(this);
                this._userManager.events.addAccessTokenExpiring(this._callback);
    
                // this will trigger loading of the user so the expiring events can be initialized
                this._userManager.getUser().then(function (user) {
                    // deliberate nop
                }).catch(function (err) {
                    // catch to suppress errors since we're in a ctor
                    _Log.Log.error("SilentRenewService.start: Error from getUser:", err.message);
                });
            }
        };
    
        SilentRenewService.prototype.stop = function stop() {
            if (this._callback) {
                this._userManager.events.removeAccessTokenExpiring(this._callback);
                delete this._callback;
            }
        };
    
        SilentRenewService.prototype._tokenExpiring = function _tokenExpiring() {
            var _this = this;
    
            this._userManager.signinSilent().then(function (user) {
                _Log.Log.debug("SilentRenewService._tokenExpiring: Silent token renewal successful");
            }, function (err) {
                _Log.Log.error("SilentRenewService._tokenExpiring: Error from signinSilent:", err.message);
                _this._userManager.events._raiseSilentRenewError(err);
            });
        };
    
        return SilentRenewService;
    }();
    
    /***/ }),
    
    /***/ "./src/State.js":
    /*!**********************!*\
      !*** ./src/State.js ***!
      \**********************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.State = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _random = __webpack_require__(/*! ./random.js */ "./src/random.js");
    
    var _random2 = _interopRequireDefault(_random);
    
    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var State = exports.State = function () {
        function State() {
            var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                id = _ref.id,
                data = _ref.data,
                created = _ref.created,
                request_type = _ref.request_type;
    
            _classCallCheck(this, State);
    
            this._id = id || (0, _random2.default)();
            this._data = data;
    
            if (typeof created === 'number' && created > 0) {
                this._created = created;
            } else {
                this._created = parseInt(Date.now() / 1000);
            }
            this._request_type = request_type;
        }
    
        State.prototype.toStorageString = function toStorageString() {
            _Log.Log.debug("State.toStorageString");
            return JSON.stringify({
                id: this.id,
                data: this.data,
                created: this.created,
                request_type: this.request_type
            });
        };
    
        State.fromStorageString = function fromStorageString(storageString) {
            _Log.Log.debug("State.fromStorageString");
            return new State(JSON.parse(storageString));
        };
    
        State.clearStaleState = function clearStaleState(storage, age) {
    
            var cutoff = Date.now() / 1000 - age;
    
            return storage.getAllKeys().then(function (keys) {
                _Log.Log.debug("State.clearStaleState: got keys", keys);
    
                var promises = [];
    
                var _loop = function _loop(i) {
                    var key = keys[i];
                    p = storage.get(key).then(function (item) {
                        var remove = false;
    
                        if (item) {
                            try {
                                var state = State.fromStorageString(item);
    
                                _Log.Log.debug("State.clearStaleState: got item from key: ", key, state.created);
    
                                if (state.created <= cutoff) {
                                    remove = true;
                                }
                            } catch (e) {
                                _Log.Log.error("State.clearStaleState: Error parsing state for key", key, e.message);
                                remove = true;
                            }
                        } else {
                            _Log.Log.debug("State.clearStaleState: no item in storage for key: ", key);
                            remove = true;
                        }
    
                        if (remove) {
                            _Log.Log.debug("State.clearStaleState: removed item for key: ", key);
                            return storage.remove(key);
                        }
                    });
    
    
                    promises.push(p);
                };
    
                for (var i = 0; i < keys.length; i++) {
                    var p;
    
                    _loop(i);
                }
    
                _Log.Log.debug("State.clearStaleState: waiting on promise count:", promises.length);
                return Promise.all(promises);
            });
        };
    
        _createClass(State, [{
            key: 'id',
            get: function get() {
                return this._id;
            }
        }, {
            key: 'data',
            get: function get() {
                return this._data;
            }
        }, {
            key: 'created',
            get: function get() {
                return this._created;
            }
        }, {
            key: 'request_type',
            get: function get() {
                return this._request_type;
            }
        }]);
    
        return State;
    }();
    
    /***/ }),
    
    /***/ "./src/Timer.js":
    /*!**********************!*\
      !*** ./src/Timer.js ***!
      \**********************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.Timer = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _Global = __webpack_require__(/*! ./Global.js */ "./src/Global.js");
    
    var _Event2 = __webpack_require__(/*! ./Event.js */ "./src/Event.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
    
    function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var TimerDuration = 5; // seconds
    
    var Timer = exports.Timer = function (_Event) {
        _inherits(Timer, _Event);
    
        function Timer(name) {
            var timer = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _Global.Global.timer;
            var nowFunc = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : undefined;
    
            _classCallCheck(this, Timer);
    
            var _this = _possibleConstructorReturn(this, _Event.call(this, name));
    
            _this._timer = timer;
    
            if (nowFunc) {
                _this._nowFunc = nowFunc;
            } else {
                _this._nowFunc = function () {
                    return Date.now() / 1000;
                };
            }
            return _this;
        }
    
        Timer.prototype.init = function init(duration) {
            if (duration <= 0) {
                duration = 1;
            }
            duration = parseInt(duration);
    
            var expiration = this.now + duration;
            if (this.expiration === expiration && this._timerHandle) {
                // no need to reinitialize to same expiration, so bail out
                _Log.Log.debug("Timer.init timer " + this._name + " skipping initialization since already initialized for expiration:", this.expiration);
                return;
            }
    
            this.cancel();
    
            _Log.Log.debug("Timer.init timer " + this._name + " for duration:", duration);
            this._expiration = expiration;
    
            // we're using a fairly short timer and then checking the expiration in the
            // callback to handle scenarios where the browser device sleeps, and then
            // the timers end up getting delayed.
            var timerDuration = TimerDuration;
            if (duration < timerDuration) {
                timerDuration = duration;
            }
            this._timerHandle = this._timer.setInterval(this._callback.bind(this), timerDuration * 1000);
        };
    
        Timer.prototype.cancel = function cancel() {
            if (this._timerHandle) {
                _Log.Log.debug("Timer.cancel: ", this._name);
                this._timer.clearInterval(this._timerHandle);
                this._timerHandle = null;
            }
        };
    
        Timer.prototype._callback = function _callback() {
            var diff = this._expiration - this.now;
            _Log.Log.debug("Timer.callback; " + this._name + " timer expires in:", diff);
    
            if (this._expiration <= this.now) {
                this.cancel();
                _Event.prototype.raise.call(this);
            }
        };
    
        _createClass(Timer, [{
            key: 'now',
            get: function get() {
                return parseInt(this._nowFunc());
            }
        }, {
            key: 'expiration',
            get: function get() {
                return this._expiration;
            }
        }]);
    
        return Timer;
    }(_Event2.Event);
    
    /***/ }),
    
    /***/ "./src/TokenClient.js":
    /*!****************************!*\
      !*** ./src/TokenClient.js ***!
      \****************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.TokenClient = undefined;
    
    var _JsonService = __webpack_require__(/*! ./JsonService.js */ "./src/JsonService.js");
    
    var _MetadataService = __webpack_require__(/*! ./MetadataService.js */ "./src/MetadataService.js");
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var TokenClient = exports.TokenClient = function () {
        function TokenClient(settings) {
            var JsonServiceCtor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _JsonService.JsonService;
            var MetadataServiceCtor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _MetadataService.MetadataService;
    
            _classCallCheck(this, TokenClient);
    
            if (!settings) {
                _Log.Log.error("TokenClient.ctor: No settings passed");
                throw new Error("settings");
            }
    
            this._settings = settings;
            this._jsonService = new JsonServiceCtor();
            this._metadataService = new MetadataServiceCtor(this._settings);
        }
    
        TokenClient.prototype.exchangeCode = function exchangeCode() {
            var _this = this;
    
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            args = Object.assign({}, args);
    
            args.grant_type = args.grant_type || "authorization_code";
            args.client_id = args.client_id || this._settings.client_id;
            args.redirect_uri = args.redirect_uri || this._settings.redirect_uri;
    
            if (!args.code) {
                _Log.Log.error("TokenClient.exchangeCode: No code passed");
                return Promise.reject(new Error("A code is required"));
            }
            if (!args.redirect_uri) {
                _Log.Log.error("TokenClient.exchangeCode: No redirect_uri passed");
                return Promise.reject(new Error("A redirect_uri is required"));
            }
            if (!args.code_verifier) {
                _Log.Log.error("TokenClient.exchangeCode: No code_verifier passed");
                return Promise.reject(new Error("A code_verifier is required"));
            }
            if (!args.client_id) {
                _Log.Log.error("TokenClient.exchangeCode: No client_id passed");
                return Promise.reject(new Error("A client_id is required"));
            }
    
            return this._metadataService.getTokenEndpoint(false).then(function (url) {
                _Log.Log.debug("TokenClient.exchangeCode: Received token endpoint");
    
                return _this._jsonService.postForm(url, args).then(function (response) {
                    _Log.Log.debug("TokenClient.exchangeCode: response received");
                    return response;
                });
            });
        };
    
        TokenClient.prototype.exchangeRefreshToken = function exchangeRefreshToken() {
            var _this2 = this;
    
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            args = Object.assign({}, args);
    
            args.grant_type = args.grant_type || "refresh_token";
            args.client_id = args.client_id || this._settings.client_id;
            args.client_secret = args.client_secret || this._settings.client_secret;
    
            if (!args.refresh_token) {
                _Log.Log.error("TokenClient.exchangeRefreshToken: No refresh_token passed");
                return Promise.reject(new Error("A refresh_token is required"));
            }
            if (!args.client_id) {
                _Log.Log.error("TokenClient.exchangeRefreshToken: No client_id passed");
                return Promise.reject(new Error("A client_id is required"));
            }
    
            return this._metadataService.getTokenEndpoint(false).then(function (url) {
                _Log.Log.debug("TokenClient.exchangeRefreshToken: Received token endpoint");
    
                return _this2._jsonService.postForm(url, args).then(function (response) {
                    _Log.Log.debug("TokenClient.exchangeRefreshToken: response received");
                    return response;
                });
            });
        };
    
        return TokenClient;
    }();
    
    /***/ }),
    
    /***/ "./src/TokenRevocationClient.js":
    /*!**************************************!*\
      !*** ./src/TokenRevocationClient.js ***!
      \**************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.TokenRevocationClient = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _MetadataService = __webpack_require__(/*! ./MetadataService.js */ "./src/MetadataService.js");
    
    var _Global = __webpack_require__(/*! ./Global.js */ "./src/Global.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var AccessTokenTypeHint = "access_token";
    var RefreshTokenTypeHint = "refresh_token";
    
    var TokenRevocationClient = exports.TokenRevocationClient = function () {
        function TokenRevocationClient(settings) {
            var XMLHttpRequestCtor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _Global.Global.XMLHttpRequest;
            var MetadataServiceCtor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _MetadataService.MetadataService;
    
            _classCallCheck(this, TokenRevocationClient);
    
            if (!settings) {
                _Log.Log.error("TokenRevocationClient.ctor: No settings provided");
                throw new Error("No settings provided.");
            }
    
            this._settings = settings;
            this._XMLHttpRequestCtor = XMLHttpRequestCtor;
            this._metadataService = new MetadataServiceCtor(this._settings);
        }
    
        TokenRevocationClient.prototype.revoke = function revoke(token, required) {
            var _this = this;
    
            var type = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : "access_token";
    
            if (!token) {
                _Log.Log.error("TokenRevocationClient.revoke: No token provided");
                throw new Error("No token provided.");
            }
    
            if (type !== AccessTokenTypeHint && type != RefreshTokenTypeHint) {
                _Log.Log.error("TokenRevocationClient.revoke: Invalid token type");
                throw new Error("Invalid token type.");
            }
    
            return this._metadataService.getRevocationEndpoint().then(function (url) {
                if (!url) {
                    if (required) {
                        _Log.Log.error("TokenRevocationClient.revoke: Revocation not supported");
                        throw new Error("Revocation not supported");
                    }
    
                    // not required, so don't error and just return
                    return;
                }
    
                _Log.Log.debug("TokenRevocationClient.revoke: Revoking " + type);
                var client_id = _this._settings.client_id;
                var client_secret = _this._settings.client_secret;
                return _this._revoke(url, client_id, client_secret, token, type);
            });
        };
    
        TokenRevocationClient.prototype._revoke = function _revoke(url, client_id, client_secret, token, type) {
            var _this2 = this;
    
            return new Promise(function (resolve, reject) {
    
                var xhr = new _this2._XMLHttpRequestCtor();
                xhr.open("POST", url);
    
                xhr.onload = function () {
                    _Log.Log.debug("TokenRevocationClient.revoke: HTTP response received, status", xhr.status);
    
                    if (xhr.status === 200) {
                        resolve();
                    } else {
                        reject(Error(xhr.statusText + " (" + xhr.status + ")"));
                    }
                };
                xhr.onerror = function () {
                    _Log.Log.debug("TokenRevocationClient.revoke: Network Error.");
                    reject("Network Error");
                };
    
                var body = "client_id=" + encodeURIComponent(client_id);
                if (client_secret) {
                    body += "&client_secret=" + encodeURIComponent(client_secret);
                }
                body += "&token_type_hint=" + encodeURIComponent(type);
                body += "&token=" + encodeURIComponent(token);
    
                xhr.setRequestHeader("Content-Type", "application/x-www-form-urlencoded");
                xhr.send(body);
            });
        };
    
        return TokenRevocationClient;
    }();
    
    /***/ }),
    
    /***/ "./src/UrlUtility.js":
    /*!***************************!*\
      !*** ./src/UrlUtility.js ***!
      \***************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.UrlUtility = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _Global = __webpack_require__(/*! ./Global.js */ "./src/Global.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var UrlUtility = exports.UrlUtility = function () {
        function UrlUtility() {
            _classCallCheck(this, UrlUtility);
        }
    
        UrlUtility.addQueryParam = function addQueryParam(url, name, value) {
            if (url.indexOf('?') < 0) {
                url += "?";
            }
    
            if (url[url.length - 1] !== "?") {
                url += "&";
            }
    
            url += encodeURIComponent(name);
            url += "=";
            url += encodeURIComponent(value);
    
            return url;
        };
    
        UrlUtility.parseUrlFragment = function parseUrlFragment(value) {
            var delimiter = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : "#";
            var global = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _Global.Global;
    
            if (typeof value !== 'string') {
                value = global.location.href;
            }
    
            var idx = value.lastIndexOf(delimiter);
            if (idx >= 0) {
                value = value.substr(idx + 1);
            }
    
            if (delimiter === "?") {
                // if we're doing query, then strip off hash fragment before we parse
                idx = value.indexOf('#');
                if (idx >= 0) {
                    value = value.substr(0, idx);
                }
            }
    
            var params = {},
                regex = /([^&=]+)=([^&]*)/g,
                m;
    
            var counter = 0;
            while (m = regex.exec(value)) {
                params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
                if (counter++ > 50) {
                    _Log.Log.error("UrlUtility.parseUrlFragment: response exceeded expected number of parameters", value);
                    return {
                        error: "Response exceeded expected number of parameters"
                    };
                }
            }
    
            for (var prop in params) {
                return params;
            }
    
            return {};
        };
    
        return UrlUtility;
    }();
    
    /***/ }),
    
    /***/ "./src/User.js":
    /*!*********************!*\
      !*** ./src/User.js ***!
      \*********************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.User = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }(); // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    var User = exports.User = function () {
        function User(_ref) {
            var id_token = _ref.id_token,
                session_state = _ref.session_state,
                access_token = _ref.access_token,
                refresh_token = _ref.refresh_token,
                token_type = _ref.token_type,
                scope = _ref.scope,
                profile = _ref.profile,
                expires_at = _ref.expires_at,
                state = _ref.state;
    
            _classCallCheck(this, User);
    
            this.id_token = id_token;
            this.session_state = session_state;
            this.access_token = access_token;
            this.refresh_token = refresh_token;
            this.token_type = token_type;
            this.scope = scope;
            this.profile = profile;
            this.expires_at = expires_at;
            this.state = state;
        }
    
        User.prototype.toStorageString = function toStorageString() {
            _Log.Log.debug("User.toStorageString");
            return JSON.stringify({
                id_token: this.id_token,
                session_state: this.session_state,
                access_token: this.access_token,
                refresh_token: this.refresh_token,
                token_type: this.token_type,
                scope: this.scope,
                profile: this.profile,
                expires_at: this.expires_at
            });
        };
    
        User.fromStorageString = function fromStorageString(storageString) {
            _Log.Log.debug("User.fromStorageString");
            return new User(JSON.parse(storageString));
        };
    
        _createClass(User, [{
            key: 'expires_in',
            get: function get() {
                if (this.expires_at) {
                    var now = parseInt(Date.now() / 1000);
                    return this.expires_at - now;
                }
                return undefined;
            },
            set: function set(value) {
                var expires_in = parseInt(value);
                if (typeof expires_in === 'number' && expires_in > 0) {
                    var now = parseInt(Date.now() / 1000);
                    this.expires_at = now + expires_in;
                }
            }
        }, {
            key: 'expired',
            get: function get() {
                var expires_in = this.expires_in;
                if (expires_in !== undefined) {
                    return expires_in <= 0;
                }
                return undefined;
            }
        }, {
            key: 'scopes',
            get: function get() {
                return (this.scope || "").split(" ");
            }
        }]);
    
        return User;
    }();
    
    /***/ }),
    
    /***/ "./src/UserInfoService.js":
    /*!********************************!*\
      !*** ./src/UserInfoService.js ***!
      \********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.UserInfoService = undefined;
    
    var _JsonService = __webpack_require__(/*! ./JsonService.js */ "./src/JsonService.js");
    
    var _MetadataService = __webpack_require__(/*! ./MetadataService.js */ "./src/MetadataService.js");
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _JoseUtil = __webpack_require__(/*! ./JoseUtil.js */ "./src/JoseUtil.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var UserInfoService = exports.UserInfoService = function () {
        function UserInfoService(settings) {
            var JsonServiceCtor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _JsonService.JsonService;
            var MetadataServiceCtor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _MetadataService.MetadataService;
            var joseUtil = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _JoseUtil.JoseUtil;
    
            _classCallCheck(this, UserInfoService);
    
            if (!settings) {
                _Log.Log.error("UserInfoService.ctor: No settings passed");
                throw new Error("settings");
            }
    
            this._settings = settings;
            this._jsonService = new JsonServiceCtor(undefined, undefined, this._getClaimsFromJwt.bind(this));
            this._metadataService = new MetadataServiceCtor(this._settings);
            this._joseUtil = joseUtil;
        }
    
        UserInfoService.prototype.getClaims = function getClaims(token) {
            var _this = this;
    
            if (!token) {
                _Log.Log.error("UserInfoService.getClaims: No token passed");
                return Promise.reject(new Error("A token is required"));
            }
    
            return this._metadataService.getUserInfoEndpoint().then(function (url) {
                _Log.Log.debug("UserInfoService.getClaims: received userinfo url", url);
    
                return _this._jsonService.getJson(url, token).then(function (claims) {
                    _Log.Log.debug("UserInfoService.getClaims: claims received", claims);
                    return claims;
                });
            });
        };
    
        UserInfoService.prototype._getClaimsFromJwt = function _getClaimsFromJwt(req) {
            var _this2 = this;
    
            try {
                var jwt = this._joseUtil.parseJwt(req.responseText);
                if (!jwt || !jwt.header || !jwt.payload) {
                    _Log.Log.error("UserInfoService._getClaimsFromJwt: Failed to parse JWT", jwt);
                    return Promise.reject(new Error("Failed to parse id_token"));
                }
    
                var kid = jwt.header.kid;
    
                var issuerPromise = void 0;
                switch (this._settings.userInfoJwtIssuer) {
                    case 'OP':
                        issuerPromise = this._metadataService.getIssuer();
                        break;
                    case 'ANY':
                        issuerPromise = Promise.resolve(jwt.payload.iss);
                        break;
                    default:
                        issuerPromise = Promise.resolve(this._settings.userInfoJwtIssuer);
                        break;
                }
    
                return issuerPromise.then(function (issuer) {
                    _Log.Log.debug("UserInfoService._getClaimsFromJwt: Received issuer:" + issuer);
    
                    return _this2._metadataService.getSigningKeys().then(function (keys) {
                        if (!keys) {
                            _Log.Log.error("UserInfoService._getClaimsFromJwt: No signing keys from metadata");
                            return Promise.reject(new Error("No signing keys from metadata"));
                        }
    
                        _Log.Log.debug("UserInfoService._getClaimsFromJwt: Received signing keys");
                        var key = void 0;
                        if (!kid) {
                            keys = _this2._filterByAlg(keys, jwt.header.alg);
    
                            if (keys.length > 1) {
                                _Log.Log.error("UserInfoService._getClaimsFromJwt: No kid found in id_token and more than one key found in metadata");
                                return Promise.reject(new Error("No kid found in id_token and more than one key found in metadata"));
                            } else {
                                // kid is mandatory only when there are multiple keys in the referenced JWK Set document
                                // see http://openid.net/specs/openid-connect-core-1_0.html#Signing
                                key = keys[0];
                            }
                        } else {
                            key = keys.filter(function (key) {
                                return key.kid === kid;
                            })[0];
                        }
    
                        if (!key) {
                            _Log.Log.error("UserInfoService._getClaimsFromJwt: No key matching kid or alg found in signing keys");
                            return Promise.reject(new Error("No key matching kid or alg found in signing keys"));
                        }
    
                        var audience = _this2._settings.client_id;
    
                        var clockSkewInSeconds = _this2._settings.clockSkew;
                        _Log.Log.debug("UserInfoService._getClaimsFromJwt: Validaing JWT; using clock skew (in seconds) of: ", clockSkewInSeconds);
    
                        return _this2._joseUtil.validateJwt(req.responseText, key, issuer, audience, clockSkewInSeconds, undefined, true).then(function () {
                            _Log.Log.debug("UserInfoService._getClaimsFromJwt: JWT validation successful");
                            return jwt.payload;
                        });
                    });
                });
                return;
            } catch (e) {
                _Log.Log.error("UserInfoService._getClaimsFromJwt: Error parsing JWT response", e.message);
                reject(e);
                return;
            }
        };
    
        UserInfoService.prototype._filterByAlg = function _filterByAlg(keys, alg) {
            var kty = null;
            if (alg.startsWith("RS")) {
                kty = "RSA";
            } else if (alg.startsWith("PS")) {
                kty = "PS";
            } else if (alg.startsWith("ES")) {
                kty = "EC";
            } else {
                _Log.Log.debug("UserInfoService._filterByAlg: alg not supported: ", alg);
                return [];
            }
    
            _Log.Log.debug("UserInfoService._filterByAlg: Looking for keys that match kty: ", kty);
    
            keys = keys.filter(function (key) {
                return key.kty === kty;
            });
    
            _Log.Log.debug("UserInfoService._filterByAlg: Number of keys that match kty: ", kty, keys.length);
    
            return keys;
        };
    
        return UserInfoService;
    }();
    
    /***/ }),
    
    /***/ "./src/UserManager.js":
    /*!****************************!*\
      !*** ./src/UserManager.js ***!
      \****************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.UserManager = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _OidcClient2 = __webpack_require__(/*! ./OidcClient.js */ "./src/OidcClient.js");
    
    var _UserManagerSettings = __webpack_require__(/*! ./UserManagerSettings.js */ "./src/UserManagerSettings.js");
    
    var _User = __webpack_require__(/*! ./User.js */ "./src/User.js");
    
    var _UserManagerEvents = __webpack_require__(/*! ./UserManagerEvents.js */ "./src/UserManagerEvents.js");
    
    var _SilentRenewService = __webpack_require__(/*! ./SilentRenewService.js */ "./src/SilentRenewService.js");
    
    var _SessionMonitor = __webpack_require__(/*! ./SessionMonitor.js */ "./src/SessionMonitor.js");
    
    var _TokenRevocationClient = __webpack_require__(/*! ./TokenRevocationClient.js */ "./src/TokenRevocationClient.js");
    
    var _TokenClient = __webpack_require__(/*! ./TokenClient.js */ "./src/TokenClient.js");
    
    var _JoseUtil = __webpack_require__(/*! ./JoseUtil.js */ "./src/JoseUtil.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
    
    function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var UserManager = exports.UserManager = function (_OidcClient) {
        _inherits(UserManager, _OidcClient);
    
        function UserManager() {
            var settings = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
            var SilentRenewServiceCtor = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : _SilentRenewService.SilentRenewService;
            var SessionMonitorCtor = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : _SessionMonitor.SessionMonitor;
            var TokenRevocationClientCtor = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : _TokenRevocationClient.TokenRevocationClient;
            var TokenClientCtor = arguments.length > 4 && arguments[4] !== undefined ? arguments[4] : _TokenClient.TokenClient;
            var joseUtil = arguments.length > 5 && arguments[5] !== undefined ? arguments[5] : _JoseUtil.JoseUtil;
    
            _classCallCheck(this, UserManager);
    
            if (!(settings instanceof _UserManagerSettings.UserManagerSettings)) {
                settings = new _UserManagerSettings.UserManagerSettings(settings);
            }
    
            var _this = _possibleConstructorReturn(this, _OidcClient.call(this, settings));
    
            _this._events = new _UserManagerEvents.UserManagerEvents(settings);
            _this._silentRenewService = new SilentRenewServiceCtor(_this);
    
            // order is important for the following properties; these services depend upon the events.
            if (_this.settings.automaticSilentRenew) {
                _Log.Log.debug("UserManager.ctor: automaticSilentRenew is configured, setting up silent renew");
                _this.startSilentRenew();
            }
    
            if (_this.settings.monitorSession) {
                _Log.Log.debug("UserManager.ctor: monitorSession is configured, setting up session monitor");
                _this._sessionMonitor = new SessionMonitorCtor(_this);
            }
    
            _this._tokenRevocationClient = new TokenRevocationClientCtor(_this._settings);
            _this._tokenClient = new TokenClientCtor(_this._settings);
            _this._joseUtil = joseUtil;
            return _this;
        }
    
        UserManager.prototype.getUser = function getUser() {
            var _this2 = this;
    
            return this._loadUser().then(function (user) {
                if (user) {
                    _Log.Log.info("UserManager.getUser: user loaded");
    
                    _this2._events.load(user, false);
    
                    return user;
                } else {
                    _Log.Log.info("UserManager.getUser: user not found in storage");
                    return null;
                }
            });
        };
    
        UserManager.prototype.removeUser = function removeUser() {
            var _this3 = this;
    
            return this.storeUser(null).then(function () {
                _Log.Log.info("UserManager.removeUser: user removed from storage");
                _this3._events.unload();
            });
        };
    
        UserManager.prototype.signinRedirect = function signinRedirect() {
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            args = Object.assign({}, args);
    
            args.request_type = "si:r";
            var navParams = {
                useReplaceToNavigate: args.useReplaceToNavigate
            };
            return this._signinStart(args, this._redirectNavigator, navParams).then(function () {
                _Log.Log.info("UserManager.signinRedirect: successful");
            });
        };
    
        UserManager.prototype.signinRedirectCallback = function signinRedirectCallback(url) {
            return this._signinEnd(url || this._redirectNavigator.url).then(function (user) {
                if (user.profile && user.profile.sub) {
                    _Log.Log.info("UserManager.signinRedirectCallback: successful, signed in sub: ", user.profile.sub);
                } else {
                    _Log.Log.info("UserManager.signinRedirectCallback: no sub");
                }
    
                return user;
            });
        };
    
        UserManager.prototype.signinPopup = function signinPopup() {
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            args = Object.assign({}, args);
    
            args.request_type = "si:p";
            var url = args.redirect_uri || this.settings.popup_redirect_uri || this.settings.redirect_uri;
            if (!url) {
                _Log.Log.error("UserManager.signinPopup: No popup_redirect_uri or redirect_uri configured");
                return Promise.reject(new Error("No popup_redirect_uri or redirect_uri configured"));
            }
    
            args.redirect_uri = url;
            args.display = "popup";
    
            return this._signin(args, this._popupNavigator, {
                startUrl: url,
                popupWindowFeatures: args.popupWindowFeatures || this.settings.popupWindowFeatures,
                popupWindowTarget: args.popupWindowTarget || this.settings.popupWindowTarget
            }).then(function (user) {
                if (user) {
                    if (user.profile && user.profile.sub) {
                        _Log.Log.info("UserManager.signinPopup: signinPopup successful, signed in sub: ", user.profile.sub);
                    } else {
                        _Log.Log.info("UserManager.signinPopup: no sub");
                    }
                }
    
                return user;
            });
        };
    
        UserManager.prototype.signinPopupCallback = function signinPopupCallback(url) {
            return this._signinCallback(url, this._popupNavigator).then(function (user) {
                if (user) {
                    if (user.profile && user.profile.sub) {
                        _Log.Log.info("UserManager.signinPopupCallback: successful, signed in sub: ", user.profile.sub);
                    } else {
                        _Log.Log.info("UserManager.signinPopupCallback: no sub");
                    }
                }
    
                return user;
            }).catch(function (err) {
                _Log.Log.error( true && err.message);
            });
        };
    
        UserManager.prototype.signinSilent = function signinSilent() {
            var _this4 = this;
    
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            args = Object.assign({}, args);
    
            args.request_type = "si:s";
            // first determine if we have a refresh token, or need to use iframe
            return this._loadUser().then(function (user) {
                if (user && user.refresh_token) {
                    args.refresh_token = user.refresh_token;
                    return _this4._useRefreshToken(args);
                } else {
                    args.id_token_hint = args.id_token_hint || _this4.settings.includeIdTokenInSilentRenew && user && user.id_token;
                    if (user && _this4._settings.validateSubOnSilentRenew) {
                        _Log.Log.debug("UserManager.signinSilent, subject prior to silent renew: ", user.profile.sub);
                        args.current_sub = user.profile.sub;
                    }
                    return _this4._signinSilentIframe(args);
                }
            });
        };
    
        UserManager.prototype._useRefreshToken = function _useRefreshToken() {
            var _this5 = this;
    
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            return this._tokenClient.exchangeRefreshToken(args).then(function (result) {
                if (!result) {
                    _Log.Log.error("UserManager._useRefreshToken: No response returned from token endpoint");
                    return Promise.reject("No response returned from token endpoint");
                }
                if (!result.access_token) {
                    _Log.Log.error("UserManager._useRefreshToken: No access token returned from token endpoint");
                    return Promise.reject("No access token returned from token endpoint");
                }
    
                return _this5._loadUser().then(function (user) {
                    if (user) {
                        var idTokenValidation = Promise.resolve();
                        if (result.id_token) {
                            idTokenValidation = _this5._validateIdTokenFromTokenRefreshToken(user.profile, result.id_token);
                        }
    
                        return idTokenValidation.then(function () {
                            _Log.Log.debug("UserManager._useRefreshToken: refresh token response success");
                            user.id_token = result.id_token;
                            user.access_token = result.access_token;
                            user.refresh_token = result.refresh_token || user.refresh_token;
                            user.expires_in = result.expires_in;
    
                            return _this5.storeUser(user).then(function () {
                                _this5._events.load(user);
                                return user;
                            });
                        });
                    } else {
                        return null;
                    }
                });
            });
        };
    
        UserManager.prototype._validateIdTokenFromTokenRefreshToken = function _validateIdTokenFromTokenRefreshToken(profile, id_token) {
            var _this6 = this;
    
            return this._metadataService.getIssuer().then(function (issuer) {
                return _this6._joseUtil.validateJwtAttributes(id_token, issuer, _this6._settings.client_id, _this6._settings.clockSkew).then(function (payload) {
                    if (!payload) {
                        _Log.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: Failed to validate id_token");
                        return Promise.reject(new Error("Failed to validate id_token"));
                    }
                    if (payload.sub !== profile.sub) {
                        _Log.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: sub in id_token does not match current sub");
                        return Promise.reject(new Error("sub in id_token does not match current sub"));
                    }
                    if (payload.auth_time && payload.auth_time !== profile.auth_time) {
                        _Log.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: auth_time in id_token does not match original auth_time");
                        return Promise.reject(new Error("auth_time in id_token does not match original auth_time"));
                    }
                    if (payload.azp && payload.azp !== profile.azp) {
                        _Log.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: azp in id_token does not match original azp");
                        return Promise.reject(new Error("azp in id_token does not match original azp"));
                    }
                    if (!payload.azp && profile.azp) {
                        _Log.Log.error("UserManager._validateIdTokenFromTokenRefreshToken: azp not in id_token, but present in original id_token");
                        return Promise.reject(new Error("azp not in id_token, but present in original id_token"));
                    }
                });
            });
        };
    
        UserManager.prototype._signinSilentIframe = function _signinSilentIframe() {
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            var url = args.redirect_uri || this.settings.silent_redirect_uri || this.settings.redirect_uri;
            if (!url) {
                _Log.Log.error("UserManager.signinSilent: No silent_redirect_uri configured");
                return Promise.reject(new Error("No silent_redirect_uri configured"));
            }
    
            args.redirect_uri = url;
            args.prompt = args.prompt || "none";
    
            return this._signin(args, this._iframeNavigator, {
                startUrl: url,
                silentRequestTimeout: args.silentRequestTimeout || this.settings.silentRequestTimeout
            }).then(function (user) {
                if (user) {
                    if (user.profile && user.profile.sub) {
                        _Log.Log.info("UserManager.signinSilent: successful, signed in sub: ", user.profile.sub);
                    } else {
                        _Log.Log.info("UserManager.signinSilent: no sub");
                    }
                }
    
                return user;
            });
        };
    
        UserManager.prototype.signinSilentCallback = function signinSilentCallback(url) {
            return this._signinCallback(url, this._iframeNavigator).then(function (user) {
                if (user) {
                    if (user.profile && user.profile.sub) {
                        _Log.Log.info("UserManager.signinSilentCallback: successful, signed in sub: ", user.profile.sub);
                    } else {
                        _Log.Log.info("UserManager.signinSilentCallback: no sub");
                    }
                }
    
                return user;
            });
        };
    
        UserManager.prototype.signinCallback = function signinCallback(url) {
            var _this7 = this;
    
            return this.readSigninResponseState(url).then(function (_ref) {
                var state = _ref.state,
                    response = _ref.response;
    
                if (state.request_type === "si:r") {
                    return _this7.signinRedirectCallback(url);
                }
                if (state.request_type === "si:p") {
                    return _this7.signinPopupCallback(url);
                }
                if (state.request_type === "si:s") {
                    return _this7.signinSilentCallback(url);
                }
                return Promise.reject(new Error("invalid response_type in state"));
            });
        };
    
        UserManager.prototype.signoutCallback = function signoutCallback(url, keepOpen) {
            var _this8 = this;
    
            return this.readSignoutResponseState(url).then(function (_ref2) {
                var state = _ref2.state,
                    response = _ref2.response;
    
                if (state) {
                    if (state.request_type === "so:r") {
                        return _this8.signoutRedirectCallback(url);
                    }
                    if (state.request_type === "so:p") {
                        return _this8.signoutPopupCallback(url, keepOpen);
                    }
                    return Promise.reject(new Error("invalid response_type in state"));
                }
                return response;
            });
        };
    
        UserManager.prototype.querySessionStatus = function querySessionStatus() {
            var _this9 = this;
    
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            args = Object.assign({}, args);
    
            args.request_type = "si:s"; // this acts like a signin silent
            var url = args.redirect_uri || this.settings.silent_redirect_uri || this.settings.redirect_uri;
            if (!url) {
                _Log.Log.error("UserManager.querySessionStatus: No silent_redirect_uri configured");
                return Promise.reject(new Error("No silent_redirect_uri configured"));
            }
    
            args.redirect_uri = url;
            args.prompt = "none";
            args.response_type = args.response_type || this.settings.query_status_response_type;
            args.scope = args.scope || "openid";
            args.skipUserInfo = true;
    
            return this._signinStart(args, this._iframeNavigator, {
                startUrl: url,
                silentRequestTimeout: args.silentRequestTimeout || this.settings.silentRequestTimeout
            }).then(function (navResponse) {
                return _this9.processSigninResponse(navResponse.url).then(function (signinResponse) {
                    _Log.Log.debug("UserManager.querySessionStatus: got signin response");
    
                    if (signinResponse.session_state && signinResponse.profile.sub) {
                        _Log.Log.info("UserManager.querySessionStatus: querySessionStatus success for sub: ", signinResponse.profile.sub);
                        return {
                            session_state: signinResponse.session_state,
                            sub: signinResponse.profile.sub,
                            sid: signinResponse.profile.sid
                        };
                    } else {
                        _Log.Log.info("querySessionStatus successful, user not authenticated");
                    }
                }).catch(function (err) {
                    if (err.session_state && _this9.settings.monitorAnonymousSession) {
                        if (err.message == "login_required" || err.message == "consent_required" || err.message == "interaction_required" || err.message == "account_selection_required") {
                            _Log.Log.info("UserManager.querySessionStatus: querySessionStatus success for anonymous user");
                            return {
                                session_state: err.session_state
                            };
                        }
                    }
    
                    throw err;
                });
            });
        };
    
        UserManager.prototype._signin = function _signin(args, navigator) {
            var _this10 = this;
    
            var navigatorParams = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    
            return this._signinStart(args, navigator, navigatorParams).then(function (navResponse) {
                return _this10._signinEnd(navResponse.url, args);
            });
        };
    
        UserManager.prototype._signinStart = function _signinStart(args, navigator) {
            var _this11 = this;
    
            var navigatorParams = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    
    
            return navigator.prepare(navigatorParams).then(function (handle) {
                _Log.Log.debug("UserManager._signinStart: got navigator window handle");
    
                return _this11.createSigninRequest(args).then(function (signinRequest) {
                    _Log.Log.debug("UserManager._signinStart: got signin request");
    
                    navigatorParams.url = signinRequest.url;
                    navigatorParams.id = signinRequest.state.id;
    
                    return handle.navigate(navigatorParams);
                }).catch(function (err) {
                    if (handle.close) {
                        _Log.Log.debug("UserManager._signinStart: Error after preparing navigator, closing navigator window");
                        handle.close();
                    }
                    throw err;
                });
            });
        };
    
        UserManager.prototype._signinEnd = function _signinEnd(url) {
            var _this12 = this;
    
            var args = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
    
            return this.processSigninResponse(url).then(function (signinResponse) {
                _Log.Log.debug("UserManager._signinEnd: got signin response");
    
                var user = new _User.User(signinResponse);
    
                if (args.current_sub) {
                    if (args.current_sub !== user.profile.sub) {
                        _Log.Log.debug("UserManager._signinEnd: current user does not match user returned from signin. sub from signin: ", user.profile.sub);
                        return Promise.reject(new Error("login_required"));
                    } else {
                        _Log.Log.debug("UserManager._signinEnd: current user matches user returned from signin");
                    }
                }
    
                return _this12.storeUser(user).then(function () {
                    _Log.Log.debug("UserManager._signinEnd: user stored");
    
                    _this12._events.load(user);
    
                    return user;
                });
            });
        };
    
        UserManager.prototype._signinCallback = function _signinCallback(url, navigator) {
            _Log.Log.debug("UserManager._signinCallback");
            return navigator.callback(url);
        };
    
        UserManager.prototype.signoutRedirect = function signoutRedirect() {
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            args = Object.assign({}, args);
    
            args.request_type = "so:r";
            var postLogoutRedirectUri = args.post_logout_redirect_uri || this.settings.post_logout_redirect_uri;
            if (postLogoutRedirectUri) {
                args.post_logout_redirect_uri = postLogoutRedirectUri;
            }
            var navParams = {
                useReplaceToNavigate: args.useReplaceToNavigate
            };
            return this._signoutStart(args, this._redirectNavigator, navParams).then(function () {
                _Log.Log.info("UserManager.signoutRedirect: successful");
            });
        };
    
        UserManager.prototype.signoutRedirectCallback = function signoutRedirectCallback(url) {
            return this._signoutEnd(url || this._redirectNavigator.url).then(function (response) {
                _Log.Log.info("UserManager.signoutRedirectCallback: successful");
                return response;
            });
        };
    
        UserManager.prototype.signoutPopup = function signoutPopup() {
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            args = Object.assign({}, args);
    
            args.request_type = "so:p";
            var url = args.post_logout_redirect_uri || this.settings.popup_post_logout_redirect_uri || this.settings.post_logout_redirect_uri;
            args.post_logout_redirect_uri = url;
            args.display = "popup";
            if (args.post_logout_redirect_uri) {
                // we're putting a dummy entry in here because we
                // need a unique id from the state for notification
                // to the parent window, which is necessary if we
                // plan to return back to the client after signout
                // and so we can close the popup after signout
                args.state = args.state || {};
            }
    
            return this._signout(args, this._popupNavigator, {
                startUrl: url,
                popupWindowFeatures: args.popupWindowFeatures || this.settings.popupWindowFeatures,
                popupWindowTarget: args.popupWindowTarget || this.settings.popupWindowTarget
            }).then(function () {
                _Log.Log.info("UserManager.signoutPopup: successful");
            });
        };
    
        UserManager.prototype.signoutPopupCallback = function signoutPopupCallback(url, keepOpen) {
            if (typeof keepOpen === 'undefined' && typeof url === 'boolean') {
                keepOpen = url;
                url = null;
            }
    
            var delimiter = '?';
            return this._popupNavigator.callback(url, keepOpen, delimiter).then(function () {
                _Log.Log.info("UserManager.signoutPopupCallback: successful");
            });
        };
    
        UserManager.prototype._signout = function _signout(args, navigator) {
            var _this13 = this;
    
            var navigatorParams = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    
            return this._signoutStart(args, navigator, navigatorParams).then(function (navResponse) {
                return _this13._signoutEnd(navResponse.url);
            });
        };
    
        UserManager.prototype._signoutStart = function _signoutStart() {
            var args = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {};
    
            var _this14 = this;
    
            var navigator = arguments[1];
            var navigatorParams = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : {};
    
            return navigator.prepare(navigatorParams).then(function (handle) {
                _Log.Log.debug("UserManager._signoutStart: got navigator window handle");
    
                return _this14._loadUser().then(function (user) {
                    _Log.Log.debug("UserManager._signoutStart: loaded current user from storage");
    
                    var revokePromise = _this14._settings.revokeAccessTokenOnSignout ? _this14._revokeInternal(user) : Promise.resolve();
                    return revokePromise.then(function () {
    
                        var id_token = args.id_token_hint || user && user.id_token;
                        if (id_token) {
                            _Log.Log.debug("UserManager._signoutStart: Setting id_token into signout request");
                            args.id_token_hint = id_token;
                        }
    
                        return _this14.removeUser().then(function () {
                            _Log.Log.debug("UserManager._signoutStart: user removed, creating signout request");
    
                            return _this14.createSignoutRequest(args).then(function (signoutRequest) {
                                _Log.Log.debug("UserManager._signoutStart: got signout request");
    
                                navigatorParams.url = signoutRequest.url;
                                if (signoutRequest.state) {
                                    navigatorParams.id = signoutRequest.state.id;
                                }
                                return handle.navigate(navigatorParams);
                            });
                        });
                    });
                }).catch(function (err) {
                    if (handle.close) {
                        _Log.Log.debug("UserManager._signoutStart: Error after preparing navigator, closing navigator window");
                        handle.close();
                    }
                    throw err;
                });
            });
        };
    
        UserManager.prototype._signoutEnd = function _signoutEnd(url) {
            return this.processSignoutResponse(url).then(function (signoutResponse) {
                _Log.Log.debug("UserManager._signoutEnd: got signout response");
    
                return signoutResponse;
            });
        };
    
        UserManager.prototype.revokeAccessToken = function revokeAccessToken() {
            var _this15 = this;
    
            return this._loadUser().then(function (user) {
                return _this15._revokeInternal(user, true).then(function (success) {
                    if (success) {
                        _Log.Log.debug("UserManager.revokeAccessToken: removing token properties from user and re-storing");
    
                        user.access_token = null;
                        user.refresh_token = null;
                        user.expires_at = null;
                        user.token_type = null;
    
                        return _this15.storeUser(user).then(function () {
                            _Log.Log.debug("UserManager.revokeAccessToken: user stored");
                            _this15._events.load(user);
                        });
                    }
                });
            }).then(function () {
                _Log.Log.info("UserManager.revokeAccessToken: access token revoked successfully");
            });
        };
    
        UserManager.prototype._revokeInternal = function _revokeInternal(user, required) {
            var _this16 = this;
    
            if (user) {
                var access_token = user.access_token;
                var refresh_token = user.refresh_token;
    
                return this._revokeAccessTokenInternal(access_token, required).then(function (atSuccess) {
                    return _this16._revokeRefreshTokenInternal(refresh_token, required).then(function (rtSuccess) {
                        if (!atSuccess && !rtSuccess) {
                            _Log.Log.debug("UserManager.revokeAccessToken: no need to revoke due to no token(s), or JWT format");
                        }
    
                        return atSuccess || rtSuccess;
                    });
                });
            }
    
            return Promise.resolve(false);
        };
    
        UserManager.prototype._revokeAccessTokenInternal = function _revokeAccessTokenInternal(access_token, required) {
            // check for JWT vs. reference token
            if (!access_token || access_token.indexOf('.') >= 0) {
                return Promise.resolve(false);
            }
    
            return this._tokenRevocationClient.revoke(access_token, required).then(function () {
                return true;
            });
        };
    
        UserManager.prototype._revokeRefreshTokenInternal = function _revokeRefreshTokenInternal(refresh_token, required) {
            if (!refresh_token) {
                return Promise.resolve(false);
            }
    
            return this._tokenRevocationClient.revoke(refresh_token, required, "refresh_token").then(function () {
                return true;
            });
        };
    
        UserManager.prototype.startSilentRenew = function startSilentRenew() {
            this._silentRenewService.start();
        };
    
        UserManager.prototype.stopSilentRenew = function stopSilentRenew() {
            this._silentRenewService.stop();
        };
    
        UserManager.prototype._loadUser = function _loadUser() {
            return this._userStore.get(this._userStoreKey).then(function (storageString) {
                if (storageString) {
                    _Log.Log.debug("UserManager._loadUser: user storageString loaded");
                    return _User.User.fromStorageString(storageString);
                }
    
                _Log.Log.debug("UserManager._loadUser: no user storageString");
                return null;
            });
        };
    
        UserManager.prototype.storeUser = function storeUser(user) {
            if (user) {
                _Log.Log.debug("UserManager.storeUser: storing user");
    
                var storageString = user.toStorageString();
                return this._userStore.set(this._userStoreKey, storageString);
            } else {
                _Log.Log.debug("storeUser.storeUser: removing user");
                return this._userStore.remove(this._userStoreKey);
            }
        };
    
        _createClass(UserManager, [{
            key: '_redirectNavigator',
            get: function get() {
                return this.settings.redirectNavigator;
            }
        }, {
            key: '_popupNavigator',
            get: function get() {
                return this.settings.popupNavigator;
            }
        }, {
            key: '_iframeNavigator',
            get: function get() {
                return this.settings.iframeNavigator;
            }
        }, {
            key: '_userStore',
            get: function get() {
                return this.settings.userStore;
            }
        }, {
            key: 'events',
            get: function get() {
                return this._events;
            }
        }, {
            key: '_userStoreKey',
            get: function get() {
                return 'user:' + this.settings.authority + ':' + this.settings.client_id;
            }
        }]);
    
        return UserManager;
    }(_OidcClient2.OidcClient);
    
    /***/ }),
    
    /***/ "./src/UserManagerEvents.js":
    /*!**********************************!*\
      !*** ./src/UserManagerEvents.js ***!
      \**********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.UserManagerEvents = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _AccessTokenEvents2 = __webpack_require__(/*! ./AccessTokenEvents.js */ "./src/AccessTokenEvents.js");
    
    var _Event = __webpack_require__(/*! ./Event.js */ "./src/Event.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
    
    function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var UserManagerEvents = exports.UserManagerEvents = function (_AccessTokenEvents) {
        _inherits(UserManagerEvents, _AccessTokenEvents);
    
        function UserManagerEvents(settings) {
            _classCallCheck(this, UserManagerEvents);
    
            var _this = _possibleConstructorReturn(this, _AccessTokenEvents.call(this, settings));
    
            _this._userLoaded = new _Event.Event("User loaded");
            _this._userUnloaded = new _Event.Event("User unloaded");
            _this._silentRenewError = new _Event.Event("Silent renew error");
            _this._userSignedIn = new _Event.Event("User signed in");
            _this._userSignedOut = new _Event.Event("User signed out");
            _this._userSessionChanged = new _Event.Event("User session changed");
            return _this;
        }
    
        UserManagerEvents.prototype.load = function load(user) {
            var raiseEvent = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : true;
    
            _Log.Log.debug("UserManagerEvents.load");
            _AccessTokenEvents.prototype.load.call(this, user);
            if (raiseEvent) {
                this._userLoaded.raise(user);
            }
        };
    
        UserManagerEvents.prototype.unload = function unload() {
            _Log.Log.debug("UserManagerEvents.unload");
            _AccessTokenEvents.prototype.unload.call(this);
            this._userUnloaded.raise();
        };
    
        UserManagerEvents.prototype.addUserLoaded = function addUserLoaded(cb) {
            this._userLoaded.addHandler(cb);
        };
    
        UserManagerEvents.prototype.removeUserLoaded = function removeUserLoaded(cb) {
            this._userLoaded.removeHandler(cb);
        };
    
        UserManagerEvents.prototype.addUserUnloaded = function addUserUnloaded(cb) {
            this._userUnloaded.addHandler(cb);
        };
    
        UserManagerEvents.prototype.removeUserUnloaded = function removeUserUnloaded(cb) {
            this._userUnloaded.removeHandler(cb);
        };
    
        UserManagerEvents.prototype.addSilentRenewError = function addSilentRenewError(cb) {
            this._silentRenewError.addHandler(cb);
        };
    
        UserManagerEvents.prototype.removeSilentRenewError = function removeSilentRenewError(cb) {
            this._silentRenewError.removeHandler(cb);
        };
    
        UserManagerEvents.prototype._raiseSilentRenewError = function _raiseSilentRenewError(e) {
            _Log.Log.debug("UserManagerEvents._raiseSilentRenewError", e.message);
            this._silentRenewError.raise(e);
        };
    
        UserManagerEvents.prototype.addUserSignedIn = function addUserSignedIn(cb) {
            this._userSignedIn.addHandler(cb);
        };
    
        UserManagerEvents.prototype.removeUserSignedIn = function removeUserSignedIn(cb) {
            this._userSignedIn.removeHandler(cb);
        };
    
        UserManagerEvents.prototype._raiseUserSignedIn = function _raiseUserSignedIn() {
            _Log.Log.debug("UserManagerEvents._raiseUserSignedIn");
            this._userSignedIn.raise();
        };
    
        UserManagerEvents.prototype.addUserSignedOut = function addUserSignedOut(cb) {
            this._userSignedOut.addHandler(cb);
        };
    
        UserManagerEvents.prototype.removeUserSignedOut = function removeUserSignedOut(cb) {
            this._userSignedOut.removeHandler(cb);
        };
    
        UserManagerEvents.prototype._raiseUserSignedOut = function _raiseUserSignedOut() {
            _Log.Log.debug("UserManagerEvents._raiseUserSignedOut");
            this._userSignedOut.raise();
        };
    
        UserManagerEvents.prototype.addUserSessionChanged = function addUserSessionChanged(cb) {
            this._userSessionChanged.addHandler(cb);
        };
    
        UserManagerEvents.prototype.removeUserSessionChanged = function removeUserSessionChanged(cb) {
            this._userSessionChanged.removeHandler(cb);
        };
    
        UserManagerEvents.prototype._raiseUserSessionChanged = function _raiseUserSessionChanged() {
            _Log.Log.debug("UserManagerEvents._raiseUserSessionChanged");
            this._userSessionChanged.raise();
        };
    
        return UserManagerEvents;
    }(_AccessTokenEvents2.AccessTokenEvents);
    
    /***/ }),
    
    /***/ "./src/UserManagerSettings.js":
    /*!************************************!*\
      !*** ./src/UserManagerSettings.js ***!
      \************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.UserManagerSettings = undefined;
    
    var _createClass = function () { function defineProperties(target, props) { for (var i = 0; i < props.length; i++) { var descriptor = props[i]; descriptor.enumerable = descriptor.enumerable || false; descriptor.configurable = true; if ("value" in descriptor) descriptor.writable = true; Object.defineProperty(target, descriptor.key, descriptor); } } return function (Constructor, protoProps, staticProps) { if (protoProps) defineProperties(Constructor.prototype, protoProps); if (staticProps) defineProperties(Constructor, staticProps); return Constructor; }; }();
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _OidcClientSettings2 = __webpack_require__(/*! ./OidcClientSettings.js */ "./src/OidcClientSettings.js");
    
    var _RedirectNavigator = __webpack_require__(/*! ./RedirectNavigator.js */ "./src/RedirectNavigator.js");
    
    var _PopupNavigator = __webpack_require__(/*! ./PopupNavigator.js */ "./src/PopupNavigator.js");
    
    var _IFrameNavigator = __webpack_require__(/*! ./IFrameNavigator.js */ "./src/IFrameNavigator.js");
    
    var _WebStorageStateStore = __webpack_require__(/*! ./WebStorageStateStore.js */ "./src/WebStorageStateStore.js");
    
    var _Global = __webpack_require__(/*! ./Global.js */ "./src/Global.js");
    
    var _SigninRequest = __webpack_require__(/*! ./SigninRequest.js */ "./src/SigninRequest.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } }
    
    function _possibleConstructorReturn(self, call) { if (!self) { throw new ReferenceError("this hasn't been initialised - super() hasn't been called"); } return call && (typeof call === "object" || typeof call === "function") ? call : self; }
    
    function _inherits(subClass, superClass) { if (typeof superClass !== "function" && superClass !== null) { throw new TypeError("Super expression must either be null or a function, not " + typeof superClass); } subClass.prototype = Object.create(superClass && superClass.prototype, { constructor: { value: subClass, enumerable: false, writable: true, configurable: true } }); if (superClass) Object.setPrototypeOf ? Object.setPrototypeOf(subClass, superClass) : subClass.__proto__ = superClass; } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var DefaultAccessTokenExpiringNotificationTime = 60;
    var DefaultCheckSessionInterval = 2000;
    
    var UserManagerSettings = exports.UserManagerSettings = function (_OidcClientSettings) {
        _inherits(UserManagerSettings, _OidcClientSettings);
    
        function UserManagerSettings() {
            var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                popup_redirect_uri = _ref.popup_redirect_uri,
                popup_post_logout_redirect_uri = _ref.popup_post_logout_redirect_uri,
                popupWindowFeatures = _ref.popupWindowFeatures,
                popupWindowTarget = _ref.popupWindowTarget,
                silent_redirect_uri = _ref.silent_redirect_uri,
                silentRequestTimeout = _ref.silentRequestTimeout,
                _ref$automaticSilentR = _ref.automaticSilentRenew,
                automaticSilentRenew = _ref$automaticSilentR === undefined ? false : _ref$automaticSilentR,
                _ref$validateSubOnSil = _ref.validateSubOnSilentRenew,
                validateSubOnSilentRenew = _ref$validateSubOnSil === undefined ? false : _ref$validateSubOnSil,
                _ref$includeIdTokenIn = _ref.includeIdTokenInSilentRenew,
                includeIdTokenInSilentRenew = _ref$includeIdTokenIn === undefined ? true : _ref$includeIdTokenIn,
                _ref$monitorSession = _ref.monitorSession,
                monitorSession = _ref$monitorSession === undefined ? true : _ref$monitorSession,
                _ref$monitorAnonymous = _ref.monitorAnonymousSession,
                monitorAnonymousSession = _ref$monitorAnonymous === undefined ? false : _ref$monitorAnonymous,
                _ref$checkSessionInte = _ref.checkSessionInterval,
                checkSessionInterval = _ref$checkSessionInte === undefined ? DefaultCheckSessionInterval : _ref$checkSessionInte,
                _ref$stopCheckSession = _ref.stopCheckSessionOnError,
                stopCheckSessionOnError = _ref$stopCheckSession === undefined ? true : _ref$stopCheckSession,
                query_status_response_type = _ref.query_status_response_type,
                _ref$revokeAccessToke = _ref.revokeAccessTokenOnSignout,
                revokeAccessTokenOnSignout = _ref$revokeAccessToke === undefined ? false : _ref$revokeAccessToke,
                _ref$accessTokenExpir = _ref.accessTokenExpiringNotificationTime,
                accessTokenExpiringNotificationTime = _ref$accessTokenExpir === undefined ? DefaultAccessTokenExpiringNotificationTime : _ref$accessTokenExpir,
                _ref$redirectNavigato = _ref.redirectNavigator,
                redirectNavigator = _ref$redirectNavigato === undefined ? new _RedirectNavigator.RedirectNavigator() : _ref$redirectNavigato,
                _ref$popupNavigator = _ref.popupNavigator,
                popupNavigator = _ref$popupNavigator === undefined ? new _PopupNavigator.PopupNavigator() : _ref$popupNavigator,
                _ref$iframeNavigator = _ref.iframeNavigator,
                iframeNavigator = _ref$iframeNavigator === undefined ? new _IFrameNavigator.IFrameNavigator() : _ref$iframeNavigator,
                _ref$userStore = _ref.userStore,
                userStore = _ref$userStore === undefined ? new _WebStorageStateStore.WebStorageStateStore({ store: _Global.Global.sessionStorage }) : _ref$userStore;
    
            _classCallCheck(this, UserManagerSettings);
    
            var _this = _possibleConstructorReturn(this, _OidcClientSettings.call(this, arguments[0]));
    
            _this._popup_redirect_uri = popup_redirect_uri;
            _this._popup_post_logout_redirect_uri = popup_post_logout_redirect_uri;
            _this._popupWindowFeatures = popupWindowFeatures;
            _this._popupWindowTarget = popupWindowTarget;
    
            _this._silent_redirect_uri = silent_redirect_uri;
            _this._silentRequestTimeout = silentRequestTimeout;
            _this._automaticSilentRenew = automaticSilentRenew;
            _this._validateSubOnSilentRenew = validateSubOnSilentRenew;
            _this._includeIdTokenInSilentRenew = includeIdTokenInSilentRenew;
            _this._accessTokenExpiringNotificationTime = accessTokenExpiringNotificationTime;
    
            _this._monitorSession = monitorSession;
            _this._monitorAnonymousSession = monitorAnonymousSession;
            _this._checkSessionInterval = checkSessionInterval;
            _this._stopCheckSessionOnError = stopCheckSessionOnError;
            if (query_status_response_type) {
                _this._query_status_response_type = query_status_response_type;
            } else if (arguments[0] && arguments[0].response_type) {
                _this._query_status_response_type = _SigninRequest.SigninRequest.isOidc(arguments[0].response_type) ? "id_token" : "code";
            } else {
                _this._query_status_response_type = "id_token";
            }
            _this._revokeAccessTokenOnSignout = revokeAccessTokenOnSignout;
    
            _this._redirectNavigator = redirectNavigator;
            _this._popupNavigator = popupNavigator;
            _this._iframeNavigator = iframeNavigator;
    
            _this._userStore = userStore;
            return _this;
        }
    
        _createClass(UserManagerSettings, [{
            key: 'popup_redirect_uri',
            get: function get() {
                return this._popup_redirect_uri;
            }
        }, {
            key: 'popup_post_logout_redirect_uri',
            get: function get() {
                return this._popup_post_logout_redirect_uri;
            }
        }, {
            key: 'popupWindowFeatures',
            get: function get() {
                return this._popupWindowFeatures;
            }
        }, {
            key: 'popupWindowTarget',
            get: function get() {
                return this._popupWindowTarget;
            }
        }, {
            key: 'silent_redirect_uri',
            get: function get() {
                return this._silent_redirect_uri;
            }
        }, {
            key: 'silentRequestTimeout',
            get: function get() {
                return this._silentRequestTimeout;
            }
        }, {
            key: 'automaticSilentRenew',
            get: function get() {
                return this._automaticSilentRenew;
            }
        }, {
            key: 'validateSubOnSilentRenew',
            get: function get() {
                return this._validateSubOnSilentRenew;
            }
        }, {
            key: 'includeIdTokenInSilentRenew',
            get: function get() {
                return this._includeIdTokenInSilentRenew;
            }
        }, {
            key: 'accessTokenExpiringNotificationTime',
            get: function get() {
                return this._accessTokenExpiringNotificationTime;
            }
        }, {
            key: 'monitorSession',
            get: function get() {
                return this._monitorSession;
            }
        }, {
            key: 'monitorAnonymousSession',
            get: function get() {
                return this._monitorAnonymousSession;
            }
        }, {
            key: 'checkSessionInterval',
            get: function get() {
                return this._checkSessionInterval;
            }
        }, {
            key: 'stopCheckSessionOnError',
            get: function get() {
                return this._stopCheckSessionOnError;
            }
        }, {
            key: 'query_status_response_type',
            get: function get() {
                return this._query_status_response_type;
            }
        }, {
            key: 'revokeAccessTokenOnSignout',
            get: function get() {
                return this._revokeAccessTokenOnSignout;
            }
        }, {
            key: 'redirectNavigator',
            get: function get() {
                return this._redirectNavigator;
            }
        }, {
            key: 'popupNavigator',
            get: function get() {
                return this._popupNavigator;
            }
        }, {
            key: 'iframeNavigator',
            get: function get() {
                return this._iframeNavigator;
            }
        }, {
            key: 'userStore',
            get: function get() {
                return this._userStore;
            }
        }]);
    
        return UserManagerSettings;
    }(_OidcClientSettings2.OidcClientSettings);
    
    /***/ }),
    
    /***/ "./src/WebStorageStateStore.js":
    /*!*************************************!*\
      !*** ./src/WebStorageStateStore.js ***!
      \*************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.WebStorageStateStore = undefined;
    
    var _Log = __webpack_require__(/*! ./Log.js */ "./src/Log.js");
    
    var _Global = __webpack_require__(/*! ./Global.js */ "./src/Global.js");
    
    function _classCallCheck(instance, Constructor) { if (!(instance instanceof Constructor)) { throw new TypeError("Cannot call a class as a function"); } } // Copyright (c) Brock Allen & Dominick Baier. All rights reserved.
    // Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information.
    
    var WebStorageStateStore = exports.WebStorageStateStore = function () {
        function WebStorageStateStore() {
            var _ref = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {},
                _ref$prefix = _ref.prefix,
                prefix = _ref$prefix === undefined ? "oidc." : _ref$prefix,
                _ref$store = _ref.store,
                store = _ref$store === undefined ? _Global.Global.localStorage : _ref$store;
    
            _classCallCheck(this, WebStorageStateStore);
    
            this._store = store;
            this._prefix = prefix;
        }
    
        WebStorageStateStore.prototype.set = function set(key, value) {
            _Log.Log.debug("WebStorageStateStore.set", key);
    
            key = this._prefix + key;
    
            this._store.setItem(key, value);
    
            return Promise.resolve();
        };
    
        WebStorageStateStore.prototype.get = function get(key) {
            _Log.Log.debug("WebStorageStateStore.get", key);
    
            key = this._prefix + key;
    
            var item = this._store.getItem(key);
    
            return Promise.resolve(item);
        };
    
        WebStorageStateStore.prototype.remove = function remove(key) {
            _Log.Log.debug("WebStorageStateStore.remove", key);
    
            key = this._prefix + key;
    
            var item = this._store.getItem(key);
            this._store.removeItem(key);
    
            return Promise.resolve(item);
        };
    
        WebStorageStateStore.prototype.getAllKeys = function getAllKeys() {
            _Log.Log.debug("WebStorageStateStore.getAllKeys");
    
            var keys = [];
    
            for (var index = 0; index < this._store.length; index++) {
                var key = this._store.key(index);
    
                if (key.indexOf(this._prefix) === 0) {
                    keys.push(key.substr(this._prefix.length));
                }
            }
    
            return Promise.resolve(keys);
        };
    
        return WebStorageStateStore;
    }();
    
    /***/ }),
    
    /***/ "./src/crypto/jsrsasign.js":
    /*!*********************************!*\
      !*** ./src/crypto/jsrsasign.js ***!
      \*********************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
        value: true
    });
    exports.AllowedSigningAlgs = exports.b64tohex = exports.hextob64u = exports.crypto = exports.X509 = exports.KeyUtil = exports.jws = undefined;
    
    var _jsrsasign = __webpack_require__(/*! ../../jsrsasign/dist/jsrsasign.js */ "./jsrsasign/dist/jsrsasign.js");
    
    var AllowedSigningAlgs = ['RS256', 'RS384', 'RS512', 'PS256', 'PS384', 'PS512', 'ES256', 'ES384', 'ES512'];
    
    exports.jws = _jsrsasign.jws;
    exports.KeyUtil = _jsrsasign.KEYUTIL;
    exports.X509 = _jsrsasign.X509;
    exports.crypto = _jsrsasign.crypto;
    exports.hextob64u = _jsrsasign.hextob64u;
    exports.b64tohex = _jsrsasign.b64tohex;
    exports.AllowedSigningAlgs = AllowedSigningAlgs;
    
    /***/ }),
    
    /***/ "./src/random.js":
    /*!***********************!*\
      !*** ./src/random.js ***!
      \***********************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    exports.default = random;
    
    var _v = __webpack_require__(/*! uuid/v4 */ "./node_modules/uuid/v4.js");
    
    var _v2 = _interopRequireDefault(_v);
    
    function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
    
    /**
     * Generates RFC4122 version 4 guid ()
     */
    
    function random() {
      return (0, _v2.default)().replace(/-/g, '');
    }
    module.exports = exports['default'];
    
    /***/ }),
    
    /***/ "./version.js":
    /*!********************!*\
      !*** ./version.js ***!
      \********************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    "use strict";
    
    
    Object.defineProperty(exports, "__esModule", {
      value: true
    });
    var Version = "1.10.1";exports.Version = Version;
    
    /***/ }),
    
    /***/ 0:
    /*!***************************************!*\
      !*** multi babel-polyfill ./index.js ***!
      \***************************************/
    /*! no static exports found */
    /***/ (function(module, exports, __webpack_require__) {
    
    __webpack_require__(/*! babel-polyfill */"./node_modules/babel-polyfill/lib/index.js");
    module.exports = __webpack_require__(/*! ./index.js */"./index.js");
    
    
    /***/ })
    
    /******/ });
    //# sourceMappingURL=data:application/json;charset=utf-8;base64,eyJ2ZXJzaW9uIjozLCJzb3VyY2VzIjpbIndlYnBhY2s6Ly9PaWRjL3dlYnBhY2svYm9vdHN0cmFwIiwid2VicGFjazovL09pZGMvLi9pbmRleC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vanNyc2FzaWduL2Rpc3QvanNyc2FzaWduLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvYmFiZWwtcG9seWZpbGwvbGliL2luZGV4LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvYmFiZWwtcG9seWZpbGwvbm9kZV9tb2R1bGVzL3JlZ2VuZXJhdG9yLXJ1bnRpbWUvcnVudGltZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2Jhc2U2NC1qcy9pbmRleC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2J1ZmZlci9pbmRleC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2J1ZmZlci9ub2RlX21vZHVsZXMvaXNhcnJheS9pbmRleC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvZm4vcmVnZXhwL2VzY2FwZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fYS1mdW5jdGlvbi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fYS1udW1iZXItdmFsdWUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2FkZC10by11bnNjb3BhYmxlcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fYWR2YW5jZS1zdHJpbmctaW5kZXguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2FuLWluc3RhbmNlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19hbi1vYmplY3QuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2FycmF5LWNvcHktd2l0aGluLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19hcnJheS1maWxsLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19hcnJheS1mcm9tLWl0ZXJhYmxlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19hcnJheS1pbmNsdWRlcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fYXJyYXktbWV0aG9kcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fYXJyYXktcmVkdWNlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19hcnJheS1zcGVjaWVzLWNvbnN0cnVjdG9yLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19hcnJheS1zcGVjaWVzLWNyZWF0ZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fYmluZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fY2xhc3NvZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fY29mLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19jb2xsZWN0aW9uLXN0cm9uZy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fY29sbGVjdGlvbi10by1qc29uLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19jb2xsZWN0aW9uLXdlYWsuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2NvbGxlY3Rpb24uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2NvcmUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2NyZWF0ZS1wcm9wZXJ0eS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fY3R4LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19kYXRlLXRvLWlzby1zdHJpbmcuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2RhdGUtdG8tcHJpbWl0aXZlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19kZWZpbmVkLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19kZXNjcmlwdG9ycy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fZG9tLWNyZWF0ZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fZW51bS1idWcta2V5cy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fZW51bS1rZXlzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19leHBvcnQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2ZhaWxzLWlzLXJlZ2V4cC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fZmFpbHMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2ZpeC1yZS13a3MuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2ZsYWdzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19mbGF0dGVuLWludG8tYXJyYXkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2Zvci1vZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fZnVuY3Rpb24tdG8tc3RyaW5nLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19nbG9iYWwuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2hhcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faGlkZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faHRtbC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faWU4LWRvbS1kZWZpbmUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2luaGVyaXQtaWYtcmVxdWlyZWQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2ludm9rZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faW9iamVjdC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faXMtYXJyYXktaXRlci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faXMtYXJyYXkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2lzLWludGVnZXIuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2lzLW9iamVjdC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faXMtcmVnZXhwLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19pdGVyLWNhbGwuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2l0ZXItY3JlYXRlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19pdGVyLWRlZmluZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faXRlci1kZXRlY3QuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX2l0ZXItc3RlcC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9faXRlcmF0b3JzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19saWJyYXJ5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19tYXRoLWV4cG0xLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19tYXRoLWZyb3VuZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fbWF0aC1sb2cxcC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fbWF0aC1zY2FsZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fbWF0aC1zaWduLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19tZXRhLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19tZXRhZGF0YS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fbWljcm90YXNrLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19uZXctcHJvbWlzZS1jYXBhYmlsaXR5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19vYmplY3QtYXNzaWduLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19vYmplY3QtY3JlYXRlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19vYmplY3QtZHAuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX29iamVjdC1kcHMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX29iamVjdC1mb3JjZWQtcGFtLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19vYmplY3QtZ29wZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fb2JqZWN0LWdvcG4tZXh0LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19vYmplY3QtZ29wbi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fb2JqZWN0LWdvcHMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX29iamVjdC1ncG8uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX29iamVjdC1rZXlzLWludGVybmFsLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19vYmplY3Qta2V5cy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fb2JqZWN0LXBpZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fb2JqZWN0LXNhcC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fb2JqZWN0LXRvLWFycmF5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19vd24ta2V5cy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fcGFyc2UtZmxvYXQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3BhcnNlLWludC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fcGVyZm9ybS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fcHJvbWlzZS1yZXNvbHZlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19wcm9wZXJ0eS1kZXNjLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19yZWRlZmluZS1hbGwuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3JlZGVmaW5lLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19yZWdleHAtZXhlYy1hYnN0cmFjdC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fcmVnZXhwLWV4ZWMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3JlcGxhY2VyLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19zYW1lLXZhbHVlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19zZXQtY29sbGVjdGlvbi1mcm9tLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19zZXQtY29sbGVjdGlvbi1vZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fc2V0LXByb3RvLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19zZXQtc3BlY2llcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fc2V0LXRvLXN0cmluZy10YWcuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3NoYXJlZC1rZXkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3NoYXJlZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fc3BlY2llcy1jb25zdHJ1Y3Rvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fc3RyaWN0LW1ldGhvZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fc3RyaW5nLWF0LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19zdHJpbmctY29udGV4dC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fc3RyaW5nLWh0bWwuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3N0cmluZy1wYWQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3N0cmluZy1yZXBlYXQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3N0cmluZy10cmltLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL19zdHJpbmctd3MuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3Rhc2suanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3RvLWFic29sdXRlLWluZGV4LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL190by1pbmRleC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fdG8taW50ZWdlci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fdG8taW9iamVjdC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fdG8tbGVuZ3RoLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL190by1vYmplY3QuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3RvLXByaW1pdGl2ZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fdHlwZWQtYXJyYXkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3R5cGVkLWJ1ZmZlci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fdHlwZWQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvX3VpZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fdXNlci1hZ2VudC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fdmFsaWRhdGUtY29sbGVjdGlvbi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fd2tzLWRlZmluZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fd2tzLWV4dC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9fd2tzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2NvcmUuZ2V0LWl0ZXJhdG9yLW1ldGhvZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9jb3JlLnJlZ2V4cC5lc2NhcGUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LmFycmF5LmNvcHktd2l0aGluLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5ldmVyeS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuYXJyYXkuZmlsbC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuYXJyYXkuZmlsdGVyLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5maW5kLWluZGV4LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5maW5kLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5mb3ItZWFjaC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuYXJyYXkuZnJvbS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuYXJyYXkuaW5kZXgtb2YuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LmFycmF5LmlzLWFycmF5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5pdGVyYXRvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuYXJyYXkuam9pbi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuYXJyYXkubGFzdC1pbmRleC1vZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuYXJyYXkubWFwLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5vZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuYXJyYXkucmVkdWNlLXJpZ2h0LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5yZWR1Y2UuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LmFycmF5LnNsaWNlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5zb21lLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5zb3J0LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5hcnJheS5zcGVjaWVzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5kYXRlLm5vdy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuZGF0ZS50by1pc28tc3RyaW5nLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5kYXRlLnRvLWpzb24uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LmRhdGUudG8tcHJpbWl0aXZlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5kYXRlLnRvLXN0cmluZy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuZnVuY3Rpb24uYmluZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuZnVuY3Rpb24uaGFzLWluc3RhbmNlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5mdW5jdGlvbi5uYW1lLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5tYXAuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm1hdGguYWNvc2guanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm1hdGguYXNpbmguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm1hdGguYXRhbmguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm1hdGguY2JydC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubWF0aC5jbHozMi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubWF0aC5jb3NoLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5tYXRoLmV4cG0xLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5tYXRoLmZyb3VuZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubWF0aC5oeXBvdC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubWF0aC5pbXVsLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5tYXRoLmxvZzEwLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5tYXRoLmxvZzFwLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5tYXRoLmxvZzIuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm1hdGguc2lnbi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubWF0aC5zaW5oLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5tYXRoLnRhbmguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm1hdGgudHJ1bmMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm51bWJlci5jb25zdHJ1Y3Rvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubnVtYmVyLmVwc2lsb24uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm51bWJlci5pcy1maW5pdGUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm51bWJlci5pcy1pbnRlZ2VyLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5udW1iZXIuaXMtbmFuLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5udW1iZXIuaXMtc2FmZS1pbnRlZ2VyLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5udW1iZXIubWF4LXNhZmUtaW50ZWdlci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubnVtYmVyLm1pbi1zYWZlLWludGVnZXIuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm51bWJlci5wYXJzZS1mbG9hdC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubnVtYmVyLnBhcnNlLWludC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYubnVtYmVyLnRvLWZpeGVkLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5udW1iZXIudG8tcHJlY2lzaW9uLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5vYmplY3QuYXNzaWduLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5vYmplY3QuY3JlYXRlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5vYmplY3QuZGVmaW5lLXByb3BlcnRpZXMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5kZWZpbmUtcHJvcGVydHkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5mcmVlemUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5nZXQtb3duLXByb3BlcnR5LWRlc2NyaXB0b3IuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5nZXQtb3duLXByb3BlcnR5LW5hbWVzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5vYmplY3QuZ2V0LXByb3RvdHlwZS1vZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYub2JqZWN0LmlzLWV4dGVuc2libGUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5pcy1mcm96ZW4uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5pcy1zZWFsZWQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5pcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYub2JqZWN0LmtleXMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5wcmV2ZW50LWV4dGVuc2lvbnMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2Lm9iamVjdC5zZWFsLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5vYmplY3Quc2V0LXByb3RvdHlwZS1vZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYub2JqZWN0LnRvLXN0cmluZy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucGFyc2UtZmxvYXQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnBhcnNlLWludC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucHJvbWlzZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucmVmbGVjdC5hcHBseS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucmVmbGVjdC5jb25zdHJ1Y3QuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnJlZmxlY3QuZGVmaW5lLXByb3BlcnR5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWZsZWN0LmRlbGV0ZS1wcm9wZXJ0eS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucmVmbGVjdC5lbnVtZXJhdGUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnJlZmxlY3QuZ2V0LW93bi1wcm9wZXJ0eS1kZXNjcmlwdG9yLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWZsZWN0LmdldC1wcm90b3R5cGUtb2YuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnJlZmxlY3QuZ2V0LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWZsZWN0Lmhhcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucmVmbGVjdC5pcy1leHRlbnNpYmxlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWZsZWN0Lm93bi1rZXlzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWZsZWN0LnByZXZlbnQtZXh0ZW5zaW9ucy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucmVmbGVjdC5zZXQtcHJvdG90eXBlLW9mLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWZsZWN0LnNldC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucmVnZXhwLmNvbnN0cnVjdG9yLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWdleHAuZXhlYy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYucmVnZXhwLmZsYWdzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWdleHAubWF0Y2guanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnJlZ2V4cC5yZXBsYWNlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWdleHAuc2VhcmNoLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5yZWdleHAuc3BsaXQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnJlZ2V4cC50by1zdHJpbmcuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnNldC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLmFuY2hvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLmJpZy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLmJsaW5rLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5zdHJpbmcuYm9sZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLmNvZGUtcG9pbnQtYXQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnN0cmluZy5lbmRzLXdpdGguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnN0cmluZy5maXhlZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLmZvbnRjb2xvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLmZvbnRzaXplLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5zdHJpbmcuZnJvbS1jb2RlLXBvaW50LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5zdHJpbmcuaW5jbHVkZXMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnN0cmluZy5pdGFsaWNzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5zdHJpbmcuaXRlcmF0b3IuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnN0cmluZy5saW5rLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5zdHJpbmcucmF3LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5zdHJpbmcucmVwZWF0LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi5zdHJpbmcuc21hbGwuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnN0cmluZy5zdGFydHMtd2l0aC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLnN0cmlrZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLnN1Yi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLnN1cC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYuc3RyaW5nLnRyaW0uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnN5bWJvbC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYudHlwZWQuYXJyYXktYnVmZmVyLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi50eXBlZC5kYXRhLXZpZXcuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnR5cGVkLmZsb2F0MzItYXJyYXkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnR5cGVkLmZsb2F0NjQtYXJyYXkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnR5cGVkLmludDE2LWFycmF5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi50eXBlZC5pbnQzMi1hcnJheS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYudHlwZWQuaW50OC1hcnJheS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYudHlwZWQudWludDE2LWFycmF5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi50eXBlZC51aW50MzItYXJyYXkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM2LnR5cGVkLnVpbnQ4LWFycmF5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi50eXBlZC51aW50OC1jbGFtcGVkLWFycmF5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNi53ZWFrLW1hcC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczYud2Vhay1zZXQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LmFycmF5LmZsYXQtbWFwLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5hcnJheS5mbGF0dGVuLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5hcnJheS5pbmNsdWRlcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcuYXNhcC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcuZXJyb3IuaXMtZXJyb3IuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lmdsb2JhbC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcubWFwLmZyb20uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hcC5vZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcubWFwLnRvLWpzb24uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hdGguY2xhbXAuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hdGguZGVnLXBlci1yYWQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hdGguZGVncmVlcy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcubWF0aC5mc2NhbGUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hdGguaWFkZGguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hdGguaW11bGguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hdGguaXN1YmguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hdGgucmFkLXBlci1kZWcuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm1hdGgucmFkaWFucy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcubWF0aC5zY2FsZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcubWF0aC5zaWduYml0LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5tYXRoLnVtdWxoLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5vYmplY3QuZGVmaW5lLWdldHRlci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcub2JqZWN0LmRlZmluZS1zZXR0ZXIuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm9iamVjdC5lbnRyaWVzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5vYmplY3QuZ2V0LW93bi1wcm9wZXJ0eS1kZXNjcmlwdG9ycy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcub2JqZWN0Lmxvb2t1cC1nZXR0ZXIuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3Lm9iamVjdC5sb29rdXAtc2V0dGVyLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5vYmplY3QudmFsdWVzLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5vYnNlcnZhYmxlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5wcm9taXNlLmZpbmFsbHkuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LnByb21pc2UudHJ5LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5yZWZsZWN0LmRlZmluZS1tZXRhZGF0YS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcucmVmbGVjdC5kZWxldGUtbWV0YWRhdGEuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LnJlZmxlY3QuZ2V0LW1ldGFkYXRhLWtleXMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LnJlZmxlY3QuZ2V0LW1ldGFkYXRhLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5yZWZsZWN0LmdldC1vd24tbWV0YWRhdGEta2V5cy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcucmVmbGVjdC5nZXQtb3duLW1ldGFkYXRhLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5yZWZsZWN0Lmhhcy1tZXRhZGF0YS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcucmVmbGVjdC5oYXMtb3duLW1ldGFkYXRhLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5yZWZsZWN0Lm1ldGFkYXRhLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5zZXQuZnJvbS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcuc2V0Lm9mLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5zZXQudG8tanNvbi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcuc3RyaW5nLmF0LmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5zdHJpbmcubWF0Y2gtYWxsLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy5zdHJpbmcucGFkLWVuZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcuc3RyaW5nLnBhZC1zdGFydC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcuc3RyaW5nLnRyaW0tbGVmdC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcuc3RyaW5nLnRyaW0tcmlnaHQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LnN5bWJvbC5hc3luYy1pdGVyYXRvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy9lczcuc3ltYm9sLm9ic2VydmFibGUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LnN5c3RlbS5nbG9iYWwuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LndlYWstbWFwLmZyb20uanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvZXM3LndlYWstbWFwLm9mLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy53ZWFrLXNldC5mcm9tLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL2VzNy53ZWFrLXNldC5vZi5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvbW9kdWxlcy93ZWIuZG9tLml0ZXJhYmxlLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvY29yZS1qcy9tb2R1bGVzL3dlYi5pbW1lZGlhdGUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy9jb3JlLWpzL21vZHVsZXMvd2ViLnRpbWVycy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2NvcmUtanMvc2hpbS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL2llZWU3NTQvaW5kZXguanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL25vZGVfbW9kdWxlcy91dWlkL2xpYi9ieXRlc1RvVXVpZC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vbm9kZV9tb2R1bGVzL3V1aWQvbGliL3JuZy1icm93c2VyLmpzIiwid2VicGFjazovL09pZGMvLi9ub2RlX21vZHVsZXMvdXVpZC92NC5qcyIsIndlYnBhY2s6Ly9PaWRjLyh3ZWJwYWNrKS9idWlsZGluL2dsb2JhbC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0FjY2Vzc1Rva2VuRXZlbnRzLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvQ2hlY2tTZXNzaW9uSUZyYW1lLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvQ29yZG92YUlGcmFtZU5hdmlnYXRvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0NvcmRvdmFQb3B1cE5hdmlnYXRvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0NvcmRvdmFQb3B1cFdpbmRvdy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0Vycm9yUmVzcG9uc2UuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9FdmVudC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0dsb2JhbC5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0lGcmFtZU5hdmlnYXRvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0lGcmFtZVdpbmRvdy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0luTWVtb3J5V2ViU3RvcmFnZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL0pvc2VVdGlsLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvSm9zZVV0aWxJbXBsLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvSnNvblNlcnZpY2UuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9Mb2cuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9NZXRhZGF0YVNlcnZpY2UuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9PaWRjQ2xpZW50LmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvT2lkY0NsaWVudFNldHRpbmdzLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvUG9wdXBOYXZpZ2F0b3IuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9Qb3B1cFdpbmRvdy5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL1JlZGlyZWN0TmF2aWdhdG9yLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvUmVzcG9uc2VWYWxpZGF0b3IuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9TZXNzaW9uTW9uaXRvci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL1NpZ25pblJlcXVlc3QuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9TaWduaW5SZXNwb25zZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL1NpZ25pblN0YXRlLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvU2lnbm91dFJlcXVlc3QuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9TaWdub3V0UmVzcG9uc2UuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9TaWxlbnRSZW5ld1NlcnZpY2UuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9TdGF0ZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL1RpbWVyLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvVG9rZW5DbGllbnQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9Ub2tlblJldm9jYXRpb25DbGllbnQuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9VcmxVdGlsaXR5LmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvVXNlci5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL1VzZXJJbmZvU2VydmljZS5qcyIsIndlYnBhY2s6Ly9PaWRjLy4vc3JjL1VzZXJNYW5hZ2VyLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvVXNlck1hbmFnZXJFdmVudHMuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9Vc2VyTWFuYWdlclNldHRpbmdzLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvV2ViU3RvcmFnZVN0YXRlU3RvcmUuanMiLCJ3ZWJwYWNrOi8vT2lkYy8uL3NyYy9jcnlwdG8vanNyc2FzaWduLmpzIiwid2VicGFjazovL09pZGMvLi9zcmMvcmFuZG9tLmpzIiwid2VicGFjazovL09pZGMvLi92ZXJzaW9uLmpzIl0sIm5hbWVzIjpbIlZlcnNpb24iLCJMb2ciLCJPaWRjQ2xpZW50IiwiT2lkY0NsaWVudFNldHRpbmdzIiwiV2ViU3RvcmFnZVN0YXRlU3RvcmUiLCJJbk1lbW9yeVdlYlN0b3JhZ2UiLCJVc2VyTWFuYWdlciIsIkFjY2Vzc1Rva2VuRXZlbnRzIiwiTWV0YWRhdGFTZXJ2aWNlIiwiQ29yZG92YVBvcHVwTmF2aWdhdG9yIiwiQ29yZG92YUlGcmFtZU5hdmlnYXRvciIsIkNoZWNrU2Vzc2lvbklGcmFtZSIsIlRva2VuUmV2b2NhdGlvbkNsaWVudCIsIlNlc3Npb25Nb25pdG9yIiwiR2xvYmFsIiwiVXNlciIsIm5hdmlnYXRvciIsInVzZXJBZ2VudCIsIndpbmRvdyIsIllBSE9PIiwidW5kZWZpbmVkIiwibGFuZyIsImV4dGVuZCIsImciLCJoIiwiZiIsIkVycm9yIiwiZCIsInByb3RvdHlwZSIsImNvbnN0cnVjdG9yIiwic3VwZXJjbGFzcyIsIk9iamVjdCIsImIiLCJlIiwiYyIsInRlc3QiLCJqIiwiaSIsImxlbmd0aCIsImwiLCJrIiwiYSIsIkNyeXB0b0pTIiwibGliIiwiQmFzZSIsIm4iLCJwIiwibyIsIm1peEluIiwiaGFzT3duUHJvcGVydHkiLCJpbml0IiwiJHN1cGVyIiwiYXBwbHkiLCJhcmd1bWVudHMiLCJjcmVhdGUiLCJ0b1N0cmluZyIsImNsb25lIiwiV29yZEFycmF5Iiwid29yZHMiLCJzaWdCeXRlcyIsInN0cmluZ2lmeSIsImNvbmNhdCIsInQiLCJxIiwicyIsImNsYW1wIiwiciIsImNlaWwiLCJjYWxsIiwic2xpY2UiLCJyYW5kb20iLCJwdXNoIiwibSIsImVuYyIsIkhleCIsImpvaW4iLCJwYXJzZSIsInBhcnNlSW50Iiwic3Vic3RyIiwiTGF0aW4xIiwiU3RyaW5nIiwiZnJvbUNoYXJDb2RlIiwiY2hhckNvZGVBdCIsIlV0ZjgiLCJkZWNvZGVVUklDb21wb25lbnQiLCJlc2NhcGUiLCJ1bmVzY2FwZSIsImVuY29kZVVSSUNvbXBvbmVudCIsIkJ1ZmZlcmVkQmxvY2tBbGdvcml0aG0iLCJyZXNldCIsIl9kYXRhIiwiX25EYXRhQnl0ZXMiLCJfYXBwZW5kIiwiX3Byb2Nlc3MiLCJ3IiwieCIsImJsb2NrU2l6ZSIsInYiLCJ1IiwibWF4IiwiX21pbkJ1ZmZlclNpemUiLCJtaW4iLCJfZG9Qcm9jZXNzQmxvY2siLCJzcGxpY2UiLCJIYXNoZXIiLCJjZmciLCJfZG9SZXNldCIsInVwZGF0ZSIsImZpbmFsaXplIiwiX2RvRmluYWxpemUiLCJfY3JlYXRlSGVscGVyIiwiX2NyZWF0ZUhtYWNIZWxwZXIiLCJITUFDIiwiYWxnbyIsIk1hdGgiLCJ4NjQiLCJXb3JkIiwiaGlnaCIsImxvdyIsInRvWDMyIiwiQmFzZTY0IiwiX21hcCIsImNoYXJBdCIsImluZGV4T2YiLCJzcXJ0IiwicG93IiwiU0hBMjU2IiwiX2hhc2giLCJmbG9vciIsIkhtYWNTSEEyNTYiLCJUIiwiZWEiLCJTSEE1MTIiLCJGIiwiRyIsIkgiLCJJIiwiSiIsIlgiLCJLIiwiWSIsIkwiLCJaIiwiTSIsIiQiLCJOIiwiYWEiLCJPIiwiYmEiLCJQIiwiY2EiLCJRIiwieiIsIkEiLCJ5IiwiVSIsIkIiLCJSIiwiQyIsIlMiLCJEIiwiViIsIkUiLCJXIiwiZmEiLCJkYSIsIkhtYWNTSEE1MTIiLCJTSEEzODQiLCJIbWFjU0hBMzg0IiwiYjY0bWFwIiwiYjY0cGFkIiwiaGV4MmI2NCIsInN1YnN0cmluZyIsImI2NHRvaGV4IiwiaW50MmNoYXIiLCJiNjR0b0JBIiwiQXJyYXkiLCJkYml0cyIsImNhbmFyeSIsImpfbG0iLCJCaWdJbnRlZ2VyIiwiZnJvbU51bWJlciIsImZyb21TdHJpbmciLCJuYmkiLCJhbTEiLCJhbTIiLCJhbTMiLCJhcHBOYW1lIiwiYW0iLCJEQiIsIkRNIiwiRFYiLCJCSV9GUCIsIkZWIiwiRjEiLCJGMiIsIkJJX1JNIiwiQklfUkMiLCJyciIsInZ2IiwiaW50QXQiLCJibnBDb3B5VG8iLCJibnBGcm9tSW50IiwibmJ2IiwiZnJvbUludCIsImJucEZyb21TdHJpbmciLCJmcm9tUmFkaXgiLCJaRVJPIiwic3ViVG8iLCJibnBDbGFtcCIsImJuVG9TdHJpbmciLCJuZWdhdGUiLCJ0b1JhZGl4IiwiYm5OZWdhdGUiLCJibkFicyIsImJuQ29tcGFyZVRvIiwibmJpdHMiLCJibkJpdExlbmd0aCIsImJucERMU2hpZnRUbyIsImJucERSU2hpZnRUbyIsImJucExTaGlmdFRvIiwiYm5wUlNoaWZ0VG8iLCJibnBTdWJUbyIsImJucE11bHRpcGx5VG8iLCJhYnMiLCJibnBTcXVhcmVUbyIsImJucERpdlJlbVRvIiwiY29weVRvIiwibFNoaWZ0VG8iLCJkbFNoaWZ0VG8iLCJjb21wYXJlVG8iLCJPTkUiLCJkclNoaWZ0VG8iLCJyU2hpZnRUbyIsImJuTW9kIiwiZGl2UmVtVG8iLCJDbGFzc2ljIiwiY0NvbnZlcnQiLCJtb2QiLCJjUmV2ZXJ0IiwiY1JlZHVjZSIsImNNdWxUbyIsIm11bHRpcGx5VG8iLCJyZWR1Y2UiLCJjU3FyVG8iLCJzcXVhcmVUbyIsImNvbnZlcnQiLCJyZXZlcnQiLCJtdWxUbyIsInNxclRvIiwiYm5wSW52RGlnaXQiLCJNb250Z29tZXJ5IiwibXAiLCJpbnZEaWdpdCIsIm1wbCIsIm1waCIsInVtIiwibXQyIiwibW9udENvbnZlcnQiLCJtb250UmV2ZXJ0IiwibW9udFJlZHVjZSIsIm1vbnRTcXJUbyIsIm1vbnRNdWxUbyIsImJucElzRXZlbiIsImJucEV4cCIsImJuTW9kUG93SW50IiwiaXNFdmVuIiwiZXhwIiwiYml0TGVuZ3RoIiwibW9kUG93SW50IiwiYm5DbG9uZSIsImJuSW50VmFsdWUiLCJibkJ5dGVWYWx1ZSIsImJuU2hvcnRWYWx1ZSIsImJucENodW5rU2l6ZSIsIkxOMiIsImxvZyIsImJuU2lnTnVtIiwiYm5wVG9SYWRpeCIsInNpZ251bSIsImNodW5rU2l6ZSIsImludFZhbHVlIiwiYm5wRnJvbVJhZGl4IiwiZE11bHRpcGx5IiwiZEFkZE9mZnNldCIsImJucEZyb21OdW1iZXIiLCJ0ZXN0Qml0IiwiYml0d2lzZVRvIiwic2hpZnRMZWZ0Iiwib3Bfb3IiLCJpc1Byb2JhYmxlUHJpbWUiLCJuZXh0Qnl0ZXMiLCJiblRvQnl0ZUFycmF5IiwiYm5FcXVhbHMiLCJibk1pbiIsImJuTWF4IiwiYm5wQml0d2lzZVRvIiwib3BfYW5kIiwiYm5BbmQiLCJibk9yIiwib3BfeG9yIiwiYm5Yb3IiLCJvcF9hbmRub3QiLCJibkFuZE5vdCIsImJuTm90IiwiYm5TaGlmdExlZnQiLCJiblNoaWZ0UmlnaHQiLCJsYml0IiwiYm5HZXRMb3dlc3RTZXRCaXQiLCJjYml0IiwiYm5CaXRDb3VudCIsImJuVGVzdEJpdCIsImJucENoYW5nZUJpdCIsImJuU2V0Qml0IiwiY2hhbmdlQml0IiwiYm5DbGVhckJpdCIsImJuRmxpcEJpdCIsImJucEFkZFRvIiwiYm5BZGQiLCJhZGRUbyIsImJuU3VidHJhY3QiLCJibk11bHRpcGx5IiwiYm5TcXVhcmUiLCJibkRpdmlkZSIsImJuUmVtYWluZGVyIiwiYm5EaXZpZGVBbmRSZW1haW5kZXIiLCJibnBETXVsdGlwbHkiLCJibnBEQWRkT2Zmc2V0IiwiTnVsbEV4cCIsIm5Ob3AiLCJuTXVsVG8iLCJuU3FyVG8iLCJiblBvdyIsImJucE11bHRpcGx5TG93ZXJUbyIsImJucE11bHRpcGx5VXBwZXJUbyIsIkJhcnJldHQiLCJyMiIsInEzIiwibXUiLCJkaXZpZGUiLCJiYXJyZXR0Q29udmVydCIsImJhcnJldHRSZXZlcnQiLCJiYXJyZXR0UmVkdWNlIiwibXVsdGlwbHlVcHBlclRvIiwibXVsdGlwbHlMb3dlclRvIiwiYmFycmV0dFNxclRvIiwiYmFycmV0dE11bFRvIiwiYm5Nb2RQb3ciLCJibkdDRCIsImdldExvd2VzdFNldEJpdCIsImJucE1vZEludCIsImJuTW9kSW52ZXJzZSIsInN1YnRyYWN0IiwiYWRkIiwibG93cHJpbWVzIiwibHBsaW0iLCJibklzUHJvYmFibGVQcmltZSIsIm1vZEludCIsIm1pbGxlclJhYmluIiwiYm5wTWlsbGVyUmFiaW4iLCJzaGlmdFJpZ2h0IiwibW9kUG93IiwiYnl0ZVZhbHVlIiwic2hvcnRWYWx1ZSIsInRvQnl0ZUFycmF5IiwiZXF1YWxzIiwiYW5kIiwib3IiLCJ4b3IiLCJhbmROb3QiLCJub3QiLCJiaXRDb3VudCIsInNldEJpdCIsImNsZWFyQml0IiwiZmxpcEJpdCIsIm11bHRpcGx5IiwicmVtYWluZGVyIiwiZGl2aWRlQW5kUmVtYWluZGVyIiwibW9kSW52ZXJzZSIsImdjZCIsInNxdWFyZSIsIkFyY2ZvdXIiLCJBUkM0aW5pdCIsIkFSQzRuZXh0IiwibmV4dCIsInBybmdfbmV3c3RhdGUiLCJybmdfcHNpemUiLCJybmdfc3RhdGUiLCJybmdfcG9vbCIsInJuZ19wcHRyIiwicm5nX3NlZWRfaW50Iiwicm5nX3NlZWRfdGltZSIsIkRhdGUiLCJnZXRUaW1lIiwiY3J5cHRvIiwibXNDcnlwdG8iLCJnZXRSYW5kb21WYWx1ZXMiLCJ1YSIsIlVpbnQ4QXJyYXkiLCJhcHBWZXJzaW9uIiwicm5nX2dldF9ieXRlIiwicm5nX2dldF9ieXRlcyIsIlNlY3VyZVJhbmRvbSIsInBhcnNlQmlnSW50IiwibGluZWJyayIsImJ5dGUySGV4IiwicGtjczFwYWQyIiwib2FlcF9tZ2YxX2FyciIsIm9hZXBfcGFkIiwiS0pVUiIsIk1lc3NhZ2VEaWdlc3QiLCJVdGlsIiwiZ2V0Q2Fub25pY2FsQWxnTmFtZSIsImdldEhhc2hMZW5ndGgiLCJoZXh0b3JzdHIiLCJoYXNoSGV4IiwicnN0cnRvaGV4IiwiUlNBS2V5IiwiZG1wMSIsImRtcTEiLCJjb2VmZiIsIlJTQVNldFB1YmxpYyIsImlzUHVibGljIiwiaXNQcml2YXRlIiwiUlNBRG9QdWJsaWMiLCJSU0FFbmNyeXB0IiwiZG9QdWJsaWMiLCJSU0FFbmNyeXB0T0FFUCIsInNldFB1YmxpYyIsImVuY3J5cHQiLCJlbmNyeXB0T0FFUCIsInR5cGUiLCJFQ0ZpZWxkRWxlbWVudEZwIiwiZmVGcEVxdWFscyIsImZlRnBUb0JpZ0ludGVnZXIiLCJmZUZwTmVnYXRlIiwiZmVGcEFkZCIsInRvQmlnSW50ZWdlciIsImZlRnBTdWJ0cmFjdCIsImZlRnBNdWx0aXBseSIsImZlRnBTcXVhcmUiLCJmZUZwRGl2aWRlIiwiRUNQb2ludEZwIiwiY3VydmUiLCJ6aW52IiwicG9pbnRGcEdldFgiLCJmcm9tQmlnSW50ZWdlciIsInBvaW50RnBHZXRZIiwicG9pbnRGcEVxdWFscyIsImlzSW5maW5pdHkiLCJwb2ludEZwSXNJbmZpbml0eSIsInBvaW50RnBOZWdhdGUiLCJwb2ludEZwQWRkIiwidHdpY2UiLCJnZXRJbmZpbml0eSIsInBvaW50RnBUd2ljZSIsInBvaW50RnBNdWx0aXBseSIsInBvaW50RnBNdWx0aXBseVR3byIsImdldFgiLCJnZXRZIiwibXVsdGlwbHlUd28iLCJFQ0N1cnZlRnAiLCJpbmZpbml0eSIsImN1cnZlRnBHZXRRIiwiY3VydmVGcEdldEEiLCJjdXJ2ZUZwR2V0QiIsImN1cnZlRnBFcXVhbHMiLCJjdXJ2ZUZwR2V0SW5maW5pdHkiLCJjdXJ2ZUZwRnJvbUJpZ0ludGVnZXIiLCJjdXJ2ZUZwRGVjb2RlUG9pbnRIZXgiLCJnZXRRIiwiZ2V0QSIsImdldEIiLCJkZWNvZGVQb2ludEhleCIsImdldEJ5dGVMZW5ndGgiLCJnZXRFbmNvZGVkIiwidG9CeXRlQXJyYXlVbnNpZ25lZCIsInVuc2hpZnQiLCJkZWNvZGVGcm9tIiwiZGVjb2RlRnJvbUhleCIsImFkZDJEIiwidHdpY2UyRCIsInZhbHVlT2YiLCJtdWx0aXBseTJEIiwiaXNPbkN1cnZlIiwidmFsaWRhdGUiLCJqc29uUGFyc2UiLCJSZWdFeHAiLCJtYXRjaCIsInJlcGxhY2UiLCJzaGlmdCIsImFzbjEiLCJBU04xVXRpbCIsImludGVnZXJUb0J5dGVIZXgiLCJiaWdJbnRUb01pblR3b3NDb21wbGVtZW50c0hleCIsImdldFBFTVN0cmluZ0Zyb21IZXgiLCJoZXh0b3BlbSIsIm5ld09iamVjdCIsIkRFUkJvb2xlYW4iLCJERVJJbnRlZ2VyIiwiREVSQml0U3RyaW5nIiwiREVST2N0ZXRTdHJpbmciLCJERVJOdWxsIiwiREVST2JqZWN0SWRlbnRpZmllciIsIkRFUkVudW1lcmF0ZWQiLCJERVJVVEY4U3RyaW5nIiwiREVSTnVtZXJpY1N0cmluZyIsIkRFUlByaW50YWJsZVN0cmluZyIsIkRFUlRlbGV0ZXhTdHJpbmciLCJERVJJQTVTdHJpbmciLCJERVJVVENUaW1lIiwiREVSR2VuZXJhbGl6ZWRUaW1lIiwiREVSU2VxdWVuY2UiLCJERVJTZXQiLCJERVJUYWdnZWRPYmplY3QiLCJrZXlzIiwiYXJyYXkiLCJ0YWciLCJleHBsaWNpdCIsIm9iaiIsImpzb25Ub0FTTjFIRVgiLCJnZXRFbmNvZGVkSGV4Iiwib2lkSGV4VG9JbnQiLCJvaWRJbnRUb0hleCIsInNwbGl0IiwiQVNOMU9iamVjdCIsImdldExlbmd0aEhleEZyb21WYWx1ZSIsImhWIiwiaFRMViIsImlzTW9kaWZpZWQiLCJnZXRGcmVzaFZhbHVlSGV4IiwiaEwiLCJoVCIsImdldFZhbHVlSGV4IiwiREVSQWJzdHJhY3RTdHJpbmciLCJnZXRTdHJpbmciLCJzZXRTdHJpbmciLCJ1dGY4dG9oZXgiLCJ0b0xvd2VyQ2FzZSIsInNldFN0cmluZ0hleCIsInN0ciIsImhleCIsIkRFUkFic3RyYWN0VGltZSIsImxvY2FsRGF0ZVRvVVRDIiwidXRjIiwiZ2V0VGltZXpvbmVPZmZzZXQiLCJmb3JtYXREYXRlIiwiemVyb1BhZGRpbmciLCJnZXRGdWxsWWVhciIsImdldE1vbnRoIiwiZ2V0RGF0ZSIsImdldEhvdXJzIiwiZ2V0TWludXRlcyIsImdldFNlY29uZHMiLCJnZXRNaWxsaXNlY29uZHMiLCJzdG9oZXgiLCJzZXRCeURhdGVWYWx1ZSIsIlVUQyIsInNldEJ5RGF0ZSIsIkRFUkFic3RyYWN0U3RydWN0dXJlZCIsInNldEJ5QVNOMU9iamVjdEFycmF5IiwiYXNuMUFycmF5IiwiYXBwZW5kQVNOMU9iamVjdCIsInNldEJ5QmlnSW50ZWdlciIsInNldEJ5SW50ZWdlciIsInNldFZhbHVlSGV4IiwiYmlnaW50Iiwic2V0SGV4VmFsdWVJbmNsdWRpbmdVbnVzZWRCaXRzIiwic2V0VW51c2VkQml0c0FuZEhleFZhbHVlIiwic2V0QnlCaW5hcnlTdHJpbmciLCJzZXRCeUJvb2xlYW5BcnJheSIsIm5ld0ZhbHNlQXJyYXkiLCJiaW4iLCJzZXRWYWx1ZU9pZFN0cmluZyIsInNldFZhbHVlTmFtZSIsIng1MDkiLCJPSUQiLCJuYW1lMm9pZCIsIm9pZCIsIm5hbWUiLCJkYXRlIiwid2l0aE1pbGxpcyIsIm1pbGxpcyIsInNvcnRGbGFnIiwic29ydCIsInNvcnRmbGFnIiwiaXNFeHBsaWNpdCIsImFzbjFPYmplY3QiLCJzZXRBU04xT2JqZWN0IiwiQVNOMUhFWCIsImdldExibGVuIiwiZ2V0TCIsImdldFZibGVuIiwiZ2V0VmlkeCIsImdldFYiLCJnZXRUTFYiLCJnZXROZXh0U2libGluZ0lkeCIsImdldENoaWxkSWR4IiwiZ2V0TnRoQ2hpbGRJZHgiLCJnZXRJZHhieUxpc3QiLCJnZXRUTFZieUxpc3QiLCJnZXRWYnlMaXN0IiwiaGV4dG9vaWRzdHIiLCJkdW1wIiwib21taXRfbG9uZ19vY3RldCIsImlzQVNOMUhFWCIsIm9pZDJuYW1lIiwiaGV4dG91dGY4Iiwib2lkbmFtZSIsIkpTT04iLCJ4NTA5RXh0TmFtZSIsImlzSGV4IiwiQmFzZTY0eCIsInN0b0JBIiwiQkF0b3MiLCJCQXRvaGV4Iiwic3RvYjY0Iiwic3RvYjY0dSIsImI2NHRvYjY0dSIsImI2NHV0b3MiLCJiNjR1dG9iNjQiLCJoZXh0b2I2NHUiLCJiNjR1dG9oZXgiLCJ1dGY4dG9iNjR1IiwiYjY0dXRvdXRmOCIsIkJ1ZmZlciIsInVyaWNtcHRvaGV4IiwiZW5jb2RlVVJJQ29tcG9uZW50QWxsIiwiaGV4dG91cmljbXAiLCJ1dGY4dG9iNjQiLCJiNjR0b3V0ZjgiLCJoZXh0b2I2NCIsImhleHRvYjY0bmwiLCJiNjRubHRvaGV4IiwicGVtdG9oZXgiLCJoZXh0b0FycmF5QnVmZmVyIiwiQXJyYXlCdWZmZXIiLCJEYXRhVmlldyIsInNldFVpbnQ4IiwiQXJyYXlCdWZmZXJ0b2hleCIsImJ5dGVMZW5ndGgiLCJnZXRVaW50OCIsInp1bHV0b21zZWMiLCJ6dWx1dG9zZWMiLCJ6dWx1dG9kYXRlIiwiZGF0ZXRvenVsdSIsImdldFVUQ0Z1bGxZZWFyIiwiZ2V0VVRDTW9udGgiLCJnZXRVVENEYXRlIiwiZ2V0VVRDSG91cnMiLCJnZXRVVENNaW51dGVzIiwiZ2V0VVRDU2Vjb25kcyIsImdldFVUQ01pbGxpc2Vjb25kcyIsImlwdjZ0b2hleCIsInJlcGVhdCIsImhleHRvaXB2NiIsImhleHRvaXAiLCJpcHRvaGV4IiwibmV3bGluZV90b1VuaXgiLCJuZXdsaW5lX3RvRG9zIiwiaXNJbnRlZ2VyIiwiaXNCYXNlNjQiLCJpc0Jhc2U2NFVSTCIsImlzSW50ZWdlckFycmF5IiwiaGV4dG9wb3NoZXgiLCJpbnRhcnlzdHJ0b2hleCIsIm1hcCIsInN0cmRpZmZpZHgiLCJESUdFU1RJTkZPSEVBRCIsInNoYTEiLCJzaGEyMjQiLCJzaGEyNTYiLCJzaGEzODQiLCJzaGE1MTIiLCJtZDIiLCJtZDUiLCJyaXBlbWQxNjAiLCJERUZBVUxUUFJPVklERVIiLCJobWFjbWQ1IiwiaG1hY3NoYTEiLCJobWFjc2hhMjI0IiwiaG1hY3NoYTI1NiIsImhtYWNzaGEzODQiLCJobWFjc2hhNTEyIiwiaG1hY3JpcGVtZDE2MCIsIk1ENXdpdGhSU0EiLCJTSEExd2l0aFJTQSIsIlNIQTIyNHdpdGhSU0EiLCJTSEEyNTZ3aXRoUlNBIiwiU0hBMzg0d2l0aFJTQSIsIlNIQTUxMndpdGhSU0EiLCJSSVBFTUQxNjB3aXRoUlNBIiwiTUQ1d2l0aEVDRFNBIiwiU0hBMXdpdGhFQ0RTQSIsIlNIQTIyNHdpdGhFQ0RTQSIsIlNIQTI1NndpdGhFQ0RTQSIsIlNIQTM4NHdpdGhFQ0RTQSIsIlNIQTUxMndpdGhFQ0RTQSIsIlJJUEVNRDE2MHdpdGhFQ0RTQSIsIlNIQTF3aXRoRFNBIiwiU0hBMjI0d2l0aERTQSIsIlNIQTI1NndpdGhEU0EiLCJNRDV3aXRoUlNBYW5kTUdGMSIsIlNIQTF3aXRoUlNBYW5kTUdGMSIsIlNIQTIyNHdpdGhSU0FhbmRNR0YxIiwiU0hBMjU2d2l0aFJTQWFuZE1HRjEiLCJTSEEzODR3aXRoUlNBYW5kTUdGMSIsIlNIQTUxMndpdGhSU0FhbmRNR0YxIiwiUklQRU1EMTYwd2l0aFJTQWFuZE1HRjEiLCJDUllQVE9KU01FU1NBR0VESUdFU1ROQU1FIiwiTUQ1IiwiU0hBMSIsIlNIQTIyNCIsIlJJUEVNRDE2MCIsImdldERpZ2VzdEluZm9IZXgiLCJnZXRQYWRkZWREaWdlc3RJbmZvSGV4IiwiaGFzaFN0cmluZyIsImFsZyIsImRpZ2VzdFN0cmluZyIsImRpZ2VzdEhleCIsInByb3YiLCJzaGEyNTZIZXgiLCJzaGE1MTJIZXgiLCJTRUNVUkVSQU5ET01HRU4iLCJnZXRSYW5kb21IZXhPZk5ieXRlcyIsImdldFJhbmRvbUJpZ0ludGVnZXJPZk5ieXRlcyIsImdldFJhbmRvbUhleE9mTmJpdHMiLCJnZXRSYW5kb21CaWdJbnRlZ2VyT2ZOYml0cyIsImdldFJhbmRvbUJpZ0ludGVnZXJaZXJvVG9NYXgiLCJnZXRSYW5kb21CaWdJbnRlZ2VyTWluVG9NYXgiLCJzZXRBbGdBbmRQcm92aWRlciIsIm1kIiwidXBkYXRlU3RyaW5nIiwidXBkYXRlSGV4IiwiZGlnZXN0Iiwic2pjbCIsImhhc2giLCJjb2RlYyIsInRvQml0cyIsImZyb21CaXRzIiwiYWxnTmFtZSIsInByb3ZOYW1lIiwiSEFTSExFTkdUSCIsIk1hYyIsImFsZ1Byb3YiLCJtYWMiLCJwYXNzIiwiZG9GaW5hbCIsImRvRmluYWxTdHJpbmciLCJkb0ZpbmFsSGV4Iiwic2V0UGFzc3dvcmQiLCJ1dGY4IiwicnN0ciIsImI2NCIsImI2NHUiLCJTaWduYXR1cmUiLCJfc2V0QWxnTmFtZXMiLCJtZEFsZ05hbWUiLCJwdWJrZXlBbGdOYW1lIiwiX3plcm9QYWRkaW5nT2ZTaWduYXR1cmUiLCJLRVlVVElMIiwiZ2V0S2V5IiwicHJ2S2V5Iiwic3RhdGUiLCJwdWJLZXkiLCJzaWduIiwic0hhc2hIZXgiLCJlY3BydmhleCIsImVjY3VydmVuYW1lIiwiRUNEU0EiLCJoU2lnbiIsInNpZ25IZXgiLCJzaWduV2l0aE1lc3NhZ2VIYXNoUFNTIiwicHNzU2FsdExlbiIsInNpZ25XaXRoTWVzc2FnZUhhc2giLCJEU0EiLCJzaWduU3RyaW5nIiwidmVyaWZ5IiwiZWNwdWJoZXgiLCJ2ZXJpZnlIZXgiLCJ2ZXJpZnlXaXRoTWVzc2FnZUhhc2hQU1MiLCJ2ZXJpZnlXaXRoTWVzc2FnZUhhc2giLCJhbGdQcm92TmFtZSIsImluaXRQYXJhbXMiLCJwc3NzYWx0bGVuIiwicHJ2a2V5cGVtIiwicHJ2a2V5cGFzIiwiQ2lwaGVyIiwiZ2V0QWxnQnlLZXlBbmROYW1lIiwiZGVjcnlwdCIsImRlY3J5cHRPQUVQIiwib2lkaGV4Mm5hbWUiLCJnZXRCaWdSYW5kb20iLCJzZXROYW1lZEN1cnZlIiwiZWNwYXJhbXMiLCJFQ1BhcmFtZXRlckRCIiwiZ2V0QnlOYW1lIiwicHJ2S2V5SGV4IiwicHViS2V5SGV4IiwiY3VydmVOYW1lIiwic2V0UHJpdmF0ZUtleUhleCIsInNldFB1YmxpY0tleUhleCIsImdldFB1YmxpY0tleVhZSGV4Iiwia2V5bGVuIiwiZ2V0U2hvcnROSVNUUEN1cnZlTmFtZSIsImdlbmVyYXRlS2V5UGFpckhleCIsImJpUlNTaWdUb0FTTjFTaWciLCJmcm9tQnl0ZUFycmF5VW5zaWduZWQiLCJzZXJpYWxpemVTaWciLCJwYXJzZVNpZ0hleCIsInZlcmlmeVJhdyIsIkJpdGNvaW4iLCJpc0FycmF5IiwicGFyc2VTaWciLCJ0b0J5dGVBcnJheVNpZ25lZCIsInBhcnNlU2lnQ29tcGFjdCIsInJlYWRQS0NTNVBydktleUhleCIsImdldE5hbWUiLCJyZWFkUEtDUzhQcnZLZXlIZXgiLCJyZWFkUEtDUzhQdWJLZXlIZXgiLCJyZWFkQ2VydFB1YktleUhleCIsInBydiIsInB1YiIsInBhcnNlU2lnSGV4SW5IZXhSUyIsImFzbjFTaWdUb0NvbmNhdFNpZyIsImNvbmNhdFNpZ1RvQVNOMVNpZyIsImhleFJTU2lnVG9BU04xU2lnIiwicmVnaXN0IiwiQUVTIiwiVHJpcGxlREVTIiwiREVTIiwia2V5IiwiaXYiLCJjaXBoZXJ0ZXh0IiwicHJvYyIsImVwcm9jIiwiaXZsZW4iLCJjaXBoZXIiLCJpdnNhbHQiLCJkYXRhIiwia2V5aGV4IiwiaXZoZXgiLCJ2ZXJzaW9uIiwicGFyc2VQS0NTNVBFTSIsImdldEtleUFuZFVudXNlZEl2QnlQYXNzY29kZUFuZEl2c2FsdCIsImRlY3J5cHRLZXlCNjQiLCJnZXREZWNyeXB0ZWRLZXlIZXgiLCJnZXRFbmNyeXB0ZWRQS0NTNVBFTUZyb21QcnZLZXlIZXgiLCJ0b1VwcGVyQ2FzZSIsInBhcnNlSGV4T2ZFbmNyeXB0ZWRQS0NTOCIsImVuY3J5cHRpb25TY2hlbWVBbGciLCJlbmNyeXB0aW9uU2NoZW1lSVYiLCJwYmtkZjJTYWx0IiwicGJrZGYySXRlciIsImdldFBCS0RGMktleUhleEZyb21QYXJhbSIsIlBCS0RGMiIsImtleVNpemUiLCJpdGVyYXRpb25zIiwiX2dldFBsYWluUEtDUzhIZXhGcm9tRW5jcnlwdGVkUEtDUzhQRU0iLCJnZXRLZXlGcm9tRW5jcnlwdGVkUEtDUzhQRU0iLCJnZXRLZXlGcm9tUGxhaW5Qcml2YXRlUEtDUzhIZXgiLCJwYXJzZVBsYWluUHJpdmF0ZVBLQ1M4SGV4IiwiYWxncGFyYW0iLCJhbGdvaWQiLCJrZXlpZHgiLCJnZXRLZXlGcm9tUGxhaW5Qcml2YXRlUEtDUzhQRU0iLCJfZ2V0S2V5RnJvbVB1YmxpY1BLQ1M4SGV4IiwicGFyc2VQdWJsaWNSYXdSU0FLZXlIZXgiLCJwYXJzZVB1YmxpY1BLQ1M4SGV4IiwieHkiLCJrdHkiLCJkcCIsImRxIiwiY28iLCJxaSIsInNldFByaXZhdGVFeCIsInNldFByaXZhdGUiLCJjcnYiLCJYNTA5IiwiZ2V0UHVibGljS2V5RnJvbUNlcnRIZXgiLCJnZXRQdWJsaWNLZXlGcm9tQ2VydFBFTSIsImdlbmVyYXRlS2V5cGFpciIsImdlbmVyYXRlIiwicHJ2S2V5T2JqIiwicHViS2V5T2JqIiwiZ2V0UEVNIiwiU3ViamVjdFB1YmxpY0tleUluZm8iLCJzZXEiLCJvY3RzdHIiLCJiaXRzdHIiLCJnZXRLZXlGcm9tQ1NSUEVNIiwiZ2V0S2V5RnJvbUNTUkhleCIsInBhcnNlQ1NSSGV4IiwicDhwdWJrZXloZXgiLCJnZXRKV0tGcm9tS2V5IiwiZ2V0UG9zQXJyYXlPZkNoaWxkcmVuRnJvbUhleCIsImdldEhleFZhbHVlQXJyYXlPZkNoaWxkcmVuRnJvbUhleCIsInJlYWRQcml2YXRlS2V5RnJvbVBFTVN0cmluZyIsInJlYWRQS0NTNVB1YktleUhleCIsInJlYWRDZXJ0SGV4IiwiZ2V0UHVibGljS2V5SGV4IiwiX1JFX0hFWERFQ09OTFkiLCJjb21waWxlIiwiX3JzYXNpZ25fZ2V0SGV4UGFkZGVkRGlnZXN0SW5mb0ZvclN0cmluZyIsImRvUHJpdmF0ZSIsInBzc19tZ2YxX3N0ciIsInNpZ25QU1MiLCJfcnNhc2lnbl9nZXREZWNyeXB0U2lnbmF0dXJlQkkiLCJfcnNhc2lnbl9nZXRIZXhEaWdlc3RJbmZvRnJvbVNpZyIsIl9yc2FzaWduX2dldEFsZ05hbWVBbmRIYXNoRnJvbUhleERpc2dlc3RJbmZvIiwidmVyaWZ5UFNTIiwiU0FMVF9MRU5fSExFTiIsIlNBTFRfTEVOX01BWCIsIlNBTFRfTEVOX1JFQ09WRVIiLCJmb2Zmc2V0IiwiYUV4dEluZm8iLCJnZXRWZXJzaW9uIiwiZ2V0U2VyaWFsTnVtYmVySGV4IiwiZ2V0U2lnbmF0dXJlQWxnb3JpdGhtRmllbGQiLCJnZXRJc3N1ZXJIZXgiLCJnZXRJc3N1ZXJTdHJpbmciLCJoZXgyZG4iLCJnZXRTdWJqZWN0SGV4IiwiZ2V0U3ViamVjdFN0cmluZyIsImdldE5vdEJlZm9yZSIsImdldE5vdEFmdGVyIiwiZ2V0UHVibGljS2V5SWR4IiwiZ2V0UHVibGljS2V5Q29udGVudElkeCIsImdldFB1YmxpY0tleSIsImdldFNpZ25hdHVyZUFsZ29yaXRobU5hbWUiLCJnZXRTaWduYXR1cmVWYWx1ZUhleCIsInZlcmlmeVNpZ25hdHVyZSIsInBhcnNlRXh0IiwiY3JpdGljYWwiLCJ2aWR4IiwiZ2V0RXh0SW5mbyIsImdldEV4dEJhc2ljQ29uc3RyYWludHMiLCJjQSIsInBhdGhMZW4iLCJnZXRFeHRLZXlVc2FnZUJpbiIsImdldEV4dEtleVVzYWdlU3RyaW5nIiwiS0VZVVNBR0VfTkFNRSIsImdldEV4dFN1YmplY3RLZXlJZGVudGlmaWVyIiwiZ2V0RXh0QXV0aG9yaXR5S2V5SWRlbnRpZmllciIsImtpZCIsImdldEV4dEV4dEtleVVzYWdlTmFtZSIsImdldEV4dFN1YmplY3RBbHROYW1lIiwiZ2V0RXh0U3ViamVjdEFsdE5hbWUyIiwiZ2V0RXh0Q1JMRGlzdHJpYnV0aW9uUG9pbnRzVVJJIiwiZ2V0RXh0QUlBSW5mbyIsIm9jc3AiLCJjYWlzc3VlciIsImdldEV4dENlcnRpZmljYXRlUG9saWNpZXMiLCJpZCIsImNwcyIsInVub3RpY2UiLCJyZWFkQ2VydFBFTSIsImdldEluZm8iLCJoZXgycmRuIiwiaGV4MmF0dHJUeXBlVmFsdWUiLCJvaWQyYXR5cGUiLCJnZXRQdWJsaWNLZXlJbmZvUHJvcE9mQ2VydFBFTSIsImp3cyIsIkpXUyIsImlzU2FmZUpTT05TdHJpbmciLCJwYXJzZUpXUyIsInBhcnNlZEpXUyIsInNpZ3ZhbEgiLCJoZWFkQjY0VSIsInBheWxvYWRCNjRVIiwic2lndmFsQjY0VSIsInNpIiwic2lndmFsQkkiLCJoZWFkUyIsInBheWxvYWRTIiwicmVhZFNhZmVKU09OU3RyaW5nIiwiandzYWxnMnNpZ2FsZyIsImhBU04xU2lnIiwiaGVhZGVyT2JqIiwicGF5bG9hZE9iaiIsImhlYWRlclBQIiwicGF5bG9hZFBQIiwic2lnSGV4IiwidmVyaWZ5SldUIiwiaW5BcnJheSIsImluY2x1ZGVkQXJyYXkiLCJpc3MiLCJzdWIiLCJhdWQiLCJJbnREYXRlIiwiZ2V0Tm93IiwidmVyaWZ5QXQiLCJncmFjZVBlcmlvZCIsIm5iZiIsImlhdCIsImp0aSIsIkhTMjU2IiwiSFMzODQiLCJIUzUxMiIsIlJTMjU2IiwiUlMzODQiLCJSUzUxMiIsIkVTMjU2IiwiRVMzODQiLCJQUzI1NiIsIlBTMzg0IiwiUFM1MTIiLCJub25lIiwiZ2V0RW5jb2RlZFNpZ25hdHVyZVZhbHVlRnJvbUpXUyIsImdldEpXS3RodW1icHJpbnQiLCJnZXQiLCJnZXRadWx1IiwiaW50RGF0ZTJVVENTdHJpbmciLCJ0b1VUQ1N0cmluZyIsImludERhdGUyWnVsdSIsIkVEU0EiLCJfY3J5cHRvIiwiRGVmYXVsdEFjY2Vzc1Rva2VuRXhwaXJpbmdOb3RpZmljYXRpb25UaW1lIiwiYWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWUiLCJhY2Nlc3NUb2tlbkV4cGlyaW5nVGltZXIiLCJUaW1lciIsImFjY2Vzc1Rva2VuRXhwaXJlZFRpbWVyIiwiX2FjY2Vzc1Rva2VuRXhwaXJpbmdOb3RpZmljYXRpb25UaW1lIiwiX2FjY2Vzc1Rva2VuRXhwaXJpbmciLCJfYWNjZXNzVG9rZW5FeHBpcmVkIiwibG9hZCIsImNvbnRhaW5lciIsImFjY2Vzc190b2tlbiIsImV4cGlyZXNfaW4iLCJkdXJhdGlvbiIsImRlYnVnIiwiZXhwaXJpbmciLCJjYW5jZWwiLCJleHBpcmVkIiwidW5sb2FkIiwiYWRkQWNjZXNzVG9rZW5FeHBpcmluZyIsImNiIiwiYWRkSGFuZGxlciIsInJlbW92ZUFjY2Vzc1Rva2VuRXhwaXJpbmciLCJyZW1vdmVIYW5kbGVyIiwiYWRkQWNjZXNzVG9rZW5FeHBpcmVkIiwicmVtb3ZlQWNjZXNzVG9rZW5FeHBpcmVkIiwiRGVmYXVsdEludGVydmFsIiwiY2FsbGJhY2siLCJjbGllbnRfaWQiLCJ1cmwiLCJpbnRlcnZhbCIsInN0b3BPbkVycm9yIiwiX2NhbGxiYWNrIiwiX2NsaWVudF9pZCIsIl91cmwiLCJfaW50ZXJ2YWwiLCJfc3RvcE9uRXJyb3IiLCJpZHgiLCJfZnJhbWVfb3JpZ2luIiwiX2ZyYW1lIiwiZG9jdW1lbnQiLCJjcmVhdGVFbGVtZW50Iiwic3R5bGUiLCJ2aXNpYmlsaXR5IiwicG9zaXRpb24iLCJkaXNwbGF5Iiwid2lkdGgiLCJoZWlnaHQiLCJzcmMiLCJQcm9taXNlIiwicmVzb2x2ZSIsIm9ubG9hZCIsImJvZHkiLCJhcHBlbmRDaGlsZCIsIl9ib3VuZE1lc3NhZ2VFdmVudCIsIl9tZXNzYWdlIiwiYmluZCIsImFkZEV2ZW50TGlzdGVuZXIiLCJvcmlnaW4iLCJzb3VyY2UiLCJjb250ZW50V2luZG93IiwiZXJyb3IiLCJzdG9wIiwic3RhcnQiLCJzZXNzaW9uX3N0YXRlIiwiX3Nlc3Npb25fc3RhdGUiLCJzZW5kIiwicG9zdE1lc3NhZ2UiLCJfdGltZXIiLCJzZXRJbnRlcnZhbCIsImNsZWFySW50ZXJ2YWwiLCJwcmVwYXJlIiwicGFyYW1zIiwicG9wdXBXaW5kb3dGZWF0dXJlcyIsInBvcHVwIiwiQ29yZG92YVBvcHVwV2luZG93IiwiRGVmYXVsdFBvcHVwRmVhdHVyZXMiLCJEZWZhdWx0UG9wdXBUYXJnZXQiLCJfcHJvbWlzZSIsInJlamVjdCIsIl9yZXNvbHZlIiwiX3JlamVjdCIsImZlYXR1cmVzIiwidGFyZ2V0IiwicG9wdXBXaW5kb3dUYXJnZXQiLCJyZWRpcmVjdF91cmkiLCJzdGFydFVybCIsIl9pc0luQXBwQnJvd3Nlckluc3RhbGxlZCIsImNvcmRvdmFNZXRhZGF0YSIsInNvbWUiLCJuYXZpZ2F0ZSIsIl9lcnJvciIsImNvcmRvdmEiLCJyZXF1aXJlIiwibWV0YWRhdGEiLCJfcG9wdXAiLCJJbkFwcEJyb3dzZXIiLCJvcGVuIiwiX2V4aXRDYWxsYmFja0V2ZW50IiwiX2V4aXRDYWxsYmFjayIsIl9sb2FkU3RhcnRDYWxsYmFja0V2ZW50IiwiX2xvYWRTdGFydENhbGxiYWNrIiwicHJvbWlzZSIsImV2ZW50IiwiX3N1Y2Nlc3MiLCJtZXNzYWdlIiwiX2NsZWFudXAiLCJjbG9zZSIsInJlbW92ZUV2ZW50TGlzdGVuZXIiLCJFcnJvclJlc3BvbnNlIiwiZXJyb3JfZGVzY3JpcHRpb24iLCJlcnJvcl91cmkiLCJFdmVudCIsIl9uYW1lIiwiX2NhbGxiYWNrcyIsImZpbmRJbmRleCIsIml0ZW0iLCJyYWlzZSIsInRpbWVyIiwiaGFuZGxlIiwidGVzdGluZyIsInJlcXVlc3QiLCJfdGVzdGluZyIsInNldFhNTEh0dHBSZXF1ZXN0IiwibmV3UmVxdWVzdCIsImxvY2F0aW9uIiwibG9jYWxTdG9yYWdlIiwic2Vzc2lvblN0b3JhZ2UiLCJYTUxIdHRwUmVxdWVzdCIsIklGcmFtZU5hdmlnYXRvciIsImZyYW1lIiwiSUZyYW1lV2luZG93Iiwibm90aWZ5UGFyZW50IiwiRGVmYXVsdFRpbWVvdXQiLCJ0aW1lb3V0Iiwic2lsZW50UmVxdWVzdFRpbWVvdXQiLCJzZXRUaW1lb3V0IiwiX3RpbWVvdXQiLCJjbGVhclRpbWVvdXQiLCJyZW1vdmVDaGlsZCIsIl9vcmlnaW4iLCJmcmFtZUVsZW1lbnQiLCJocmVmIiwicGFyZW50IiwicHJvdG9jb2wiLCJob3N0IiwiZ2V0SXRlbSIsInNldEl0ZW0iLCJ2YWx1ZSIsInJlbW92ZUl0ZW0iLCJpbmRleCIsImdldE93blByb3BlcnR5TmFtZXMiLCJKb3NlVXRpbCIsIktleVV0aWwiLCJBbGxvd2VkU2lnbmluZ0FsZ3MiLCJnZXRKb3NlVXRpbCIsInBhcnNlSnd0Iiwiand0IiwidG9rZW4iLCJoZWFkZXIiLCJwYXlsb2FkIiwidmFsaWRhdGVKd3QiLCJpc3N1ZXIiLCJhdWRpZW5jZSIsImNsb2NrU2tldyIsIm5vdyIsInRpbWVJbnNlbnNpdGl2ZSIsIng1YyIsIl92YWxpZGF0ZUp3dCIsInZhbGlkYXRlSnd0QXR0cmlidXRlcyIsInZhbGlkQXVkaWVuY2UiLCJhenAiLCJsb3dlck5vdyIsInVwcGVyTm93IiwidGhlbiIsImhleFRvQmFzZTY0VXJsIiwiSnNvblNlcnZpY2UiLCJhZGRpdGlvbmFsQ29udGVudFR5cGVzIiwiWE1MSHR0cFJlcXVlc3RDdG9yIiwiand0SGFuZGxlciIsIl9jb250ZW50VHlwZXMiLCJfWE1MSHR0cFJlcXVlc3QiLCJfand0SGFuZGxlciIsImdldEpzb24iLCJyZXEiLCJhbGxvd2VkQ29udGVudFR5cGVzIiwic3RhdHVzIiwiY29udGVudFR5cGUiLCJnZXRSZXNwb25zZUhlYWRlciIsImZvdW5kIiwiZmluZCIsInN0YXJ0c1dpdGgiLCJyZXNwb25zZVRleHQiLCJzdGF0dXNUZXh0Iiwib25lcnJvciIsInNldFJlcXVlc3RIZWFkZXIiLCJwb3N0Rm9ybSIsIm5vcExvZ2dlciIsImluZm8iLCJ3YXJuIiwiTk9ORSIsIkVSUk9SIiwiV0FSTiIsIklORk8iLCJERUJVRyIsImxvZ2dlciIsImxldmVsIiwiYXJncyIsImZyb20iLCJPaWRjTWV0YWRhdGFVcmxQYXRoIiwic2V0dGluZ3MiLCJKc29uU2VydmljZUN0b3IiLCJfc2V0dGluZ3MiLCJfanNvblNlcnZpY2UiLCJnZXRNZXRhZGF0YSIsIm1ldGFkYXRhVXJsIiwiZ2V0SXNzdWVyIiwiX2dldE1ldGFkYXRhUHJvcGVydHkiLCJnZXRBdXRob3JpemF0aW9uRW5kcG9pbnQiLCJnZXRVc2VySW5mb0VuZHBvaW50IiwiZ2V0VG9rZW5FbmRwb2ludCIsIm9wdGlvbmFsIiwiZ2V0Q2hlY2tTZXNzaW9uSWZyYW1lIiwiZ2V0RW5kU2Vzc2lvbkVuZHBvaW50IiwiZ2V0UmV2b2NhdGlvbkVuZHBvaW50IiwiZ2V0S2V5c0VuZHBvaW50IiwiZ2V0U2lnbmluZ0tleXMiLCJzaWduaW5nS2V5cyIsImp3a3NfdXJpIiwia2V5U2V0IiwiX21ldGFkYXRhVXJsIiwiYXV0aG9yaXR5IiwiY3JlYXRlU2lnbmluUmVxdWVzdCIsInJlc3BvbnNlX3R5cGUiLCJzY29wZSIsInByb21wdCIsIm1heF9hZ2UiLCJ1aV9sb2NhbGVzIiwiaWRfdG9rZW5faGludCIsImxvZ2luX2hpbnQiLCJhY3JfdmFsdWVzIiwicmVzb3VyY2UiLCJyZXF1ZXN0X3VyaSIsInJlc3BvbnNlX21vZGUiLCJleHRyYVF1ZXJ5UGFyYW1zIiwiZXh0cmFUb2tlblBhcmFtcyIsInJlcXVlc3RfdHlwZSIsInNraXBVc2VySW5mbyIsInN0YXRlU3RvcmUiLCJTaWduaW5SZXF1ZXN0IiwiaXNDb2RlIiwiX21ldGFkYXRhU2VydmljZSIsInNpZ25pblJlcXVlc3QiLCJjbGllbnRfc2VjcmV0Iiwic2lnbmluU3RhdGUiLCJfc3RhdGVTdG9yZSIsInNldCIsInRvU3RvcmFnZVN0cmluZyIsInJlYWRTaWduaW5SZXNwb25zZVN0YXRlIiwicmVtb3ZlU3RhdGUiLCJ1c2VRdWVyeSIsImRlbGltaXRlciIsInJlc3BvbnNlIiwiU2lnbmluUmVzcG9uc2UiLCJzdGF0ZUFwaSIsInJlbW92ZSIsInN0b3JlZFN0YXRlU3RyaW5nIiwiU2lnbmluU3RhdGUiLCJmcm9tU3RvcmFnZVN0cmluZyIsInByb2Nlc3NTaWduaW5SZXNwb25zZSIsIl92YWxpZGF0b3IiLCJ2YWxpZGF0ZVNpZ25pblJlc3BvbnNlIiwiY3JlYXRlU2lnbm91dFJlcXVlc3QiLCJwb3N0X2xvZ291dF9yZWRpcmVjdF91cmkiLCJTaWdub3V0UmVxdWVzdCIsInNpZ25vdXRTdGF0ZSIsInJlYWRTaWdub3V0UmVzcG9uc2VTdGF0ZSIsIlNpZ25vdXRSZXNwb25zZSIsInN0YXRlS2V5IiwiU3RhdGUiLCJwcm9jZXNzU2lnbm91dFJlc3BvbnNlIiwidmFsaWRhdGVTaWdub3V0UmVzcG9uc2UiLCJjbGVhclN0YWxlU3RhdGUiLCJzdGFsZVN0YXRlQWdlIiwidmFsaWRhdG9yIiwibWV0YWRhdGFTZXJ2aWNlIiwiRGVmYXVsdFJlc3BvbnNlVHlwZSIsIkRlZmF1bHRTY29wZSIsIkRlZmF1bHRTdGFsZVN0YXRlQWdlIiwiRGVmYXVsdENsb2NrU2tld0luU2Vjb25kcyIsImZpbHRlclByb3RvY29sQ2xhaW1zIiwibG9hZFVzZXJJbmZvIiwidXNlckluZm9Kd3RJc3N1ZXIiLCJSZXNwb25zZVZhbGlkYXRvckN0b3IiLCJSZXNwb25zZVZhbGlkYXRvciIsIk1ldGFkYXRhU2VydmljZUN0b3IiLCJfYXV0aG9yaXR5IiwiX21ldGFkYXRhIiwiX3NpZ25pbmdLZXlzIiwiX2NsaWVudF9zZWNyZXQiLCJfcmVzcG9uc2VfdHlwZSIsIl9zY29wZSIsIl9yZWRpcmVjdF91cmkiLCJfcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpIiwiX3Byb21wdCIsIl9kaXNwbGF5IiwiX21heF9hZ2UiLCJfdWlfbG9jYWxlcyIsIl9hY3JfdmFsdWVzIiwiX3Jlc291cmNlIiwiX3Jlc3BvbnNlX21vZGUiLCJfZmlsdGVyUHJvdG9jb2xDbGFpbXMiLCJfbG9hZFVzZXJJbmZvIiwiX3N0YWxlU3RhdGVBZ2UiLCJfY2xvY2tTa2V3IiwiX3VzZXJJbmZvSnd0SXNzdWVyIiwiX2V4dHJhUXVlcnlQYXJhbXMiLCJfZXh0cmFUb2tlblBhcmFtcyIsIlBvcHVwTmF2aWdhdG9yIiwiUG9wdXBXaW5kb3ciLCJrZWVwT3BlbiIsIm5vdGlmeU9wZW5lciIsIkNoZWNrRm9yUG9wdXBDbG9zZWRJbnRlcnZhbCIsIl9jaGVja0ZvclBvcHVwQ2xvc2VkVGltZXIiLCJfY2hlY2tGb3JQb3B1cENsb3NlZCIsIl9pZCIsImZvY3VzIiwiY2xvc2VkIiwib3BlbmVyIiwiVXJsVXRpbGl0eSIsInBhcnNlVXJsRnJhZ21lbnQiLCJSZWRpcmVjdE5hdmlnYXRvciIsInVzZVJlcGxhY2VUb05hdmlnYXRlIiwiUHJvdG9jb2xDbGFpbXMiLCJVc2VySW5mb1NlcnZpY2VDdG9yIiwiVXNlckluZm9TZXJ2aWNlIiwiam9zZVV0aWwiLCJUb2tlbkNsaWVudEN0b3IiLCJUb2tlbkNsaWVudCIsIl91c2VySW5mb1NlcnZpY2UiLCJfam9zZVV0aWwiLCJfdG9rZW5DbGllbnQiLCJfcHJvY2Vzc1NpZ25pblBhcmFtcyIsIl92YWxpZGF0ZVRva2VucyIsIl9wcm9jZXNzQ2xhaW1zIiwibm9uY2UiLCJpZF90b2tlbiIsImNvZGVfdmVyaWZpZXIiLCJjb2RlIiwiaXNPcGVuSWRDb25uZWN0IiwicHJvZmlsZSIsImdldENsYWltcyIsImNsYWltcyIsIl9tZXJnZUNsYWltcyIsImNsYWltczEiLCJjbGFpbXMyIiwicmVzdWx0IiwiYXNzaWduIiwidmFsdWVzIiwiZm9yRWFjaCIsIl9wcm9jZXNzQ29kZSIsIl92YWxpZGF0ZUlkVG9rZW5BbmRBY2Nlc3NUb2tlbiIsIl92YWxpZGF0ZUlkVG9rZW4iLCJleGNoYW5nZUNvZGUiLCJ0b2tlblJlc3BvbnNlIiwiX3ZhbGlkYXRlSWRUb2tlbkF0dHJpYnV0ZXMiLCJjbG9ja1NrZXdJblNlY29uZHMiLCJfdmFsaWRhdGVBY2Nlc3NUb2tlbiIsIl9maWx0ZXJCeUFsZyIsImZpbHRlciIsImF0X2hhc2giLCJoYXNoQWxnIiwiaGFzaEJpdHMiLCJzaGEiLCJsZWZ0IiwibGVmdF9iNjR1IiwidXNlck1hbmFnZXIiLCJDaGVja1Nlc3Npb25JRnJhbWVDdG9yIiwiX3VzZXJNYW5hZ2VyIiwiX0NoZWNrU2Vzc2lvbklGcmFtZUN0b3IiLCJldmVudHMiLCJhZGRVc2VyTG9hZGVkIiwiX3N0YXJ0IiwiYWRkVXNlclVubG9hZGVkIiwiX3N0b3AiLCJnZXRVc2VyIiwidXNlciIsIm1vbml0b3JBbm9ueW1vdXNTZXNzaW9uIiwicXVlcnlTZXNzaW9uU3RhdHVzIiwidG1wVXNlciIsInNlc3Npb24iLCJzaWQiLCJjYXRjaCIsImVyciIsIl9zdWIiLCJfc2lkIiwiX2NoZWNrU2Vzc2lvbklGcmFtZSIsIl9jaGVja1Nlc3Npb25JbnRlcnZhbCIsIl9zdG9wQ2hlY2tTZXNzaW9uT25FcnJvciIsInRpbWVySGFuZGxlIiwicmFpc2VFdmVudCIsIl9yYWlzZVVzZXJTZXNzaW9uQ2hhbmdlZCIsIl9yYWlzZVVzZXJTaWduZWRPdXQiLCJfcmFpc2VVc2VyU2lnbmVkSW4iLCJjaGVja1Nlc3Npb25JbnRlcnZhbCIsInN0b3BDaGVja1Nlc3Npb25PbkVycm9yIiwib2lkYyIsImlzT2lkYyIsImFkZFF1ZXJ5UGFyYW0iLCJjb2RlX2NoYWxsZW5nZSIsImlzT0F1dGgiLCJPaWRjU2NvcGUiLCJ0b2tlbl90eXBlIiwiZXhwaXJlc19hdCIsInNjb3BlcyIsIl9ub25jZSIsIl9jb2RlX3ZlcmlmaWVyIiwiX2NvZGVfY2hhbGxlbmdlIiwiX3NraXBVc2VySW5mbyIsImNyZWF0ZWQiLCJzdG9yYWdlU3RyaW5nIiwiU2lsZW50UmVuZXdTZXJ2aWNlIiwiX3Rva2VuRXhwaXJpbmciLCJzaWduaW5TaWxlbnQiLCJfcmFpc2VTaWxlbnRSZW5ld0Vycm9yIiwiX2NyZWF0ZWQiLCJfcmVxdWVzdF90eXBlIiwic3RvcmFnZSIsImFnZSIsImN1dG9mZiIsImdldEFsbEtleXMiLCJwcm9taXNlcyIsImFsbCIsIlRpbWVyRHVyYXRpb24iLCJub3dGdW5jIiwiX25vd0Z1bmMiLCJleHBpcmF0aW9uIiwiX3RpbWVySGFuZGxlIiwiX2V4cGlyYXRpb24iLCJ0aW1lckR1cmF0aW9uIiwiZGlmZiIsImdyYW50X3R5cGUiLCJleGNoYW5nZVJlZnJlc2hUb2tlbiIsInJlZnJlc2hfdG9rZW4iLCJBY2Nlc3NUb2tlblR5cGVIaW50IiwiUmVmcmVzaFRva2VuVHlwZUhpbnQiLCJfWE1MSHR0cFJlcXVlc3RDdG9yIiwicmV2b2tlIiwicmVxdWlyZWQiLCJfcmV2b2tlIiwieGhyIiwiZ2xvYmFsIiwibGFzdEluZGV4T2YiLCJyZWdleCIsImNvdW50ZXIiLCJleGVjIiwicHJvcCIsIl9nZXRDbGFpbXNGcm9tSnd0IiwiaXNzdWVyUHJvbWlzZSIsIlNpbGVudFJlbmV3U2VydmljZUN0b3IiLCJTZXNzaW9uTW9uaXRvckN0b3IiLCJUb2tlblJldm9jYXRpb25DbGllbnRDdG9yIiwiVXNlck1hbmFnZXJTZXR0aW5ncyIsIl9ldmVudHMiLCJVc2VyTWFuYWdlckV2ZW50cyIsIl9zaWxlbnRSZW5ld1NlcnZpY2UiLCJhdXRvbWF0aWNTaWxlbnRSZW5ldyIsInN0YXJ0U2lsZW50UmVuZXciLCJtb25pdG9yU2Vzc2lvbiIsIl9zZXNzaW9uTW9uaXRvciIsIl90b2tlblJldm9jYXRpb25DbGllbnQiLCJfbG9hZFVzZXIiLCJyZW1vdmVVc2VyIiwic3RvcmVVc2VyIiwic2lnbmluUmVkaXJlY3QiLCJuYXZQYXJhbXMiLCJfc2lnbmluU3RhcnQiLCJfcmVkaXJlY3ROYXZpZ2F0b3IiLCJzaWduaW5SZWRpcmVjdENhbGxiYWNrIiwiX3NpZ25pbkVuZCIsInNpZ25pblBvcHVwIiwicG9wdXBfcmVkaXJlY3RfdXJpIiwiX3NpZ25pbiIsIl9wb3B1cE5hdmlnYXRvciIsInNpZ25pblBvcHVwQ2FsbGJhY2siLCJfc2lnbmluQ2FsbGJhY2siLCJfdXNlUmVmcmVzaFRva2VuIiwiaW5jbHVkZUlkVG9rZW5JblNpbGVudFJlbmV3IiwidmFsaWRhdGVTdWJPblNpbGVudFJlbmV3IiwiY3VycmVudF9zdWIiLCJfc2lnbmluU2lsZW50SWZyYW1lIiwiaWRUb2tlblZhbGlkYXRpb24iLCJfdmFsaWRhdGVJZFRva2VuRnJvbVRva2VuUmVmcmVzaFRva2VuIiwiYXV0aF90aW1lIiwic2lsZW50X3JlZGlyZWN0X3VyaSIsIl9pZnJhbWVOYXZpZ2F0b3IiLCJzaWduaW5TaWxlbnRDYWxsYmFjayIsInNpZ25pbkNhbGxiYWNrIiwic2lnbm91dENhbGxiYWNrIiwic2lnbm91dFJlZGlyZWN0Q2FsbGJhY2siLCJzaWdub3V0UG9wdXBDYWxsYmFjayIsInF1ZXJ5X3N0YXR1c19yZXNwb25zZV90eXBlIiwibmF2UmVzcG9uc2UiLCJzaWduaW5SZXNwb25zZSIsIm5hdmlnYXRvclBhcmFtcyIsInNpZ25vdXRSZWRpcmVjdCIsInBvc3RMb2dvdXRSZWRpcmVjdFVyaSIsIl9zaWdub3V0U3RhcnQiLCJfc2lnbm91dEVuZCIsInNpZ25vdXRQb3B1cCIsInBvcHVwX3Bvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaSIsIl9zaWdub3V0IiwicmV2b2tlUHJvbWlzZSIsInJldm9rZUFjY2Vzc1Rva2VuT25TaWdub3V0IiwiX3Jldm9rZUludGVybmFsIiwic2lnbm91dFJlcXVlc3QiLCJzaWdub3V0UmVzcG9uc2UiLCJyZXZva2VBY2Nlc3NUb2tlbiIsInN1Y2Nlc3MiLCJfcmV2b2tlQWNjZXNzVG9rZW5JbnRlcm5hbCIsIl9yZXZva2VSZWZyZXNoVG9rZW5JbnRlcm5hbCIsImF0U3VjY2VzcyIsInJ0U3VjY2VzcyIsInN0b3BTaWxlbnRSZW5ldyIsIl91c2VyU3RvcmUiLCJfdXNlclN0b3JlS2V5IiwicmVkaXJlY3ROYXZpZ2F0b3IiLCJwb3B1cE5hdmlnYXRvciIsImlmcmFtZU5hdmlnYXRvciIsInVzZXJTdG9yZSIsIl91c2VyTG9hZGVkIiwiX3VzZXJVbmxvYWRlZCIsIl9zaWxlbnRSZW5ld0Vycm9yIiwiX3VzZXJTaWduZWRJbiIsIl91c2VyU2lnbmVkT3V0IiwiX3VzZXJTZXNzaW9uQ2hhbmdlZCIsInJlbW92ZVVzZXJMb2FkZWQiLCJyZW1vdmVVc2VyVW5sb2FkZWQiLCJhZGRTaWxlbnRSZW5ld0Vycm9yIiwicmVtb3ZlU2lsZW50UmVuZXdFcnJvciIsImFkZFVzZXJTaWduZWRJbiIsInJlbW92ZVVzZXJTaWduZWRJbiIsImFkZFVzZXJTaWduZWRPdXQiLCJyZW1vdmVVc2VyU2lnbmVkT3V0IiwiYWRkVXNlclNlc3Npb25DaGFuZ2VkIiwicmVtb3ZlVXNlclNlc3Npb25DaGFuZ2VkIiwiRGVmYXVsdENoZWNrU2Vzc2lvbkludGVydmFsIiwic3RvcmUiLCJfcG9wdXBfcmVkaXJlY3RfdXJpIiwiX3BvcHVwX3Bvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaSIsIl9wb3B1cFdpbmRvd0ZlYXR1cmVzIiwiX3BvcHVwV2luZG93VGFyZ2V0IiwiX3NpbGVudF9yZWRpcmVjdF91cmkiLCJfc2lsZW50UmVxdWVzdFRpbWVvdXQiLCJfYXV0b21hdGljU2lsZW50UmVuZXciLCJfdmFsaWRhdGVTdWJPblNpbGVudFJlbmV3IiwiX2luY2x1ZGVJZFRva2VuSW5TaWxlbnRSZW5ldyIsIl9tb25pdG9yU2Vzc2lvbiIsIl9tb25pdG9yQW5vbnltb3VzU2Vzc2lvbiIsIl9xdWVyeV9zdGF0dXNfcmVzcG9uc2VfdHlwZSIsIl9yZXZva2VBY2Nlc3NUb2tlbk9uU2lnbm91dCIsInByZWZpeCIsIl9zdG9yZSIsIl9wcmVmaXgiXSwibWFwcGluZ3MiOiI7O0FBQUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7OztBQUdBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxrREFBMEMsZ0NBQWdDO0FBQzFFO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsZ0VBQXdELGtCQUFrQjtBQUMxRTtBQUNBLHlEQUFpRCxjQUFjO0FBQy9EOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpREFBeUMsaUNBQWlDO0FBQzFFLHdIQUFnSCxtQkFBbUIsRUFBRTtBQUNySTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLG1DQUEyQiwwQkFBMEIsRUFBRTtBQUN2RCx5Q0FBaUMsZUFBZTtBQUNoRDtBQUNBO0FBQ0E7O0FBRUE7QUFDQSw4REFBc0QsK0RBQStEOztBQUVySDtBQUNBOzs7QUFHQTtBQUNBOzs7Ozs7Ozs7Ozs7Ozs7Ozs7O0FDL0VBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUNBOztBQUVBOztBQW5CQTtBQUNBOztrQkFvQmU7QUFDWEEsNkJBRFc7QUFFWEMsaUJBRlc7QUFHWEMsc0NBSFc7QUFJWEMsOERBSlc7QUFLWEMsb0VBTFc7QUFNWEMsOERBTlc7QUFPWEMseUNBUFc7QUFRWEMsMkRBUlc7QUFTWEMscURBVFc7QUFVWEMsdUVBVlc7QUFXWEMsMEVBWFc7QUFZWEMsOERBWlc7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QUNBLFlBQVk7QUFDWjs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsV0FBVztBQUNYO0FBQ0EsV0FBVztBQUNYOztBQUVBO0FBQ0E7QUFDQSx3Q0FBd0MsV0FBVztBQUNuRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0EsMkJBQTJCO0FBQzNCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUEsU0FBUztBQUNUO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBLFNBQVM7QUFDVDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSxvQ0FBb0MsY0FBYztBQUNsRDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxLQUFLO0FBQ0w7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsaUNBQWlDLGtCQUFrQjtBQUNuRDtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsaUJBQWlCOztBQUVqQjtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSx3QkFBd0IsaUJBQWlCO0FBQ3pDO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0EsWUFBWTtBQUNaO0FBQ0E7O0FBRUE7QUFDQSxZQUFZO0FBQ1o7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLOztBQUVMO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEtBQUs7O0FBRUw7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQSw4Q0FBOEMsUUFBUTtBQUN0RDtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsYUFBYTtBQUNiO0FBQ0E7O0FBRUEsV0FBVztBQUNYO0FBQ0E7QUFDQTs7QUFFQSxXQUFXO0FBQ1g7QUFDQTtBQUNBOztBQUVBLFdBQVc7QUFDWDtBQUNBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7O0FBRUw7QUFDQSw4Q0FBOEMsUUFBUTtBQUN0RDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLEtBQUs7O0FBRUw7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsT0FBTztBQUNQO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBOztBQUVBO0FBQ0EsS0FBSzs7QUFFTDtBQUNBLDhDQUE4QyxRQUFRO0FBQ3REO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSzs7QUFFTDtBQUNBLDhDQUE4QyxRQUFRO0FBQ3REO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7O0FBRUw7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsQ0FBQztBQUNEO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOzs7Ozs7Ozs7Ozs7OztBQy90Qlk7O0FBRVo7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLGtDQUFrQyxTQUFTO0FBQzNDO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQSxpQkFBaUIsU0FBUztBQUMxQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EscUJBQXFCLFNBQVM7QUFDOUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSwwQ0FBMEMsVUFBVTtBQUNwRDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7Ozs7Ozs7Ozs7OztBQ3RKQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFWTs7QUFFWixhQUFhLG1CQUFPLENBQUMsb0RBQVc7QUFDaEMsY0FBYyxtQkFBTyxDQUFDLGdEQUFTO0FBQy9CLGNBQWMsbUJBQU8sQ0FBQyxvRUFBUzs7QUFFL0I7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLHFCQUFxQixtREFBbUQ7QUFDeEU7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLG1CQUFtQixVQUFVO0FBQzdCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLGlCQUFpQixZQUFZO0FBQzdCO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0EsR0FBRztBQUNIO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQSwwQkFBMEI7QUFDMUI7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUEsdUNBQXVDLFNBQVM7QUFDaEQ7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsZUFBZSxpQkFBaUI7QUFDaEM7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxhQUFhLGlCQUFpQjtBQUM5QjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSxTQUFTO0FBQ1Q7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxpQkFBaUIsU0FBUztBQUMxQjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaUJBQWlCLFNBQVM7QUFDMUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsaUJBQWlCLFNBQVM7QUFDMUI7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxnREFBZ0QsRUFBRTtBQUNsRDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQSxpQkFBaUIsU0FBUztBQUMxQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EseUNBQXlDO0FBQ3pDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUDtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSx3QkFBd0IsZUFBZTtBQUN2QztBQUNBO0FBQ0E7QUFDQSxPQUFPO0FBQ1A7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQSx3QkFBd0IsUUFBUTtBQUNoQztBQUNBLHFCQUFxQixlQUFlO0FBQ3BDO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLGlCQUFpQixZQUFZO0FBQzdCO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0EsU0FBUztBQUNUO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFFQSxxQkFBcUIsU0FBUztBQUM5QjtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7O0FBRUEscUJBQXFCLFNBQVM7QUFDOUI7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0EscUJBQXFCLFNBQVM7QUFDOUI7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsaUJBQWlCLGtCQUFrQjtBQUNuQztBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTs7QUFFQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0EsbUJBQW1CLGNBQWM7QUFDakM7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBLHVEQUF1RCxPQUFPO0FBQzlEO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQSx1REFBdUQsT0FBTztBQUM5RDtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQSxHQUFHO0FBQ0g7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsR0FBRztBQUNIO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTs7QUFF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O0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTCxtREFBbUQ7QUFDbkQ7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTCxvQ0FBb0M7QUFDcEM7QUFDQSxLQUFLO0FBQ0wsd0VBQXdFO0FBQ3hFO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0wsOERBQThEO0FBQzlEO0FBQ0EsS0FBSztBQUNMLHdFQUF3RTtBQUN4RTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLE9BQU87QUFDUCxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMOztBQUVBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBLEdBQUc7O0FBRUgseUJBQXlCLHNCQUFzQixFQUFFLEVBQUU7QUFDbkQ7QUFDQTtBQUNBO0FBQ0E7O0FBRUEsNENBQTRDO0FBQzVDO0FB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QTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0E7QUFDQSxRQUFRLFdBQVc7QUFDbkI7QUFDQTtBQUNBLFFBQVEsVUFBVTtBQUNsQjtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLFFBQVEsV0FBVztBQUNuQjtBQUNBO0FBQ0E7QUFDQSxRQUFRLFdBQVc7QUFDbkI7QUFDQTtBQUNBLEdBQUc7QUFDSDtBQUNBLEdBQUc7QUFDSDtBQUNBO0FBQ0EsR0FBRztBQUNIOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBLHlCQUF5QixtQkFBbUIsdUJBQXVCLEVBQUUsRUFBRTtBQUN2RTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBLGlCQUFpQixXQUFXO0FBQzVCOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBOztBQUVBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7QUFDQTs7QUFFQTtBQUNBO0FBQ0E7QUFDQTtBQUNBO0FBQ0E7O0FBRUE7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FBQ0E7QUFDQSxLQUFLO0FBQ0w7QUFDQTtBQUNBLEtBQUs7QUFDTDtBQUNBO0FBQ0EsS0FBSztBQUNMO0FB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QWtEO0FBQzFELG1CQUFPLENBQUMsNEdBQXVDO0FBQy9DLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsd0hBQTZDO0FBQ3JELG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsZ0hBQXlDO0FBQ2pELG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsc0dBQW9DO0FBQzVDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsNEdBQXVDO0FBQy9DLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsd0dBQXFDO0FBQzdDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsNEZBQStCO0FBQ3ZDLG1CQUFPLENBQUMsb0dBQW1DO0FBQzNDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsMEdBQXNDO0FBQzlDLG1CQUFPLENBQUMsNEdBQXVDO0FBQy9DLG1CQUFPLENBQUMsNEdBQXVDO0FBQy9DLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsMEdBQXNDO0FBQzlDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsNEZBQStCO0FBQ3ZDLG1CQUFPLENBQUMsc0dBQW9DO0FBQzVDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsNEZBQStCO0FBQ3ZDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsNEZBQStCO0FBQ3ZDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsOEVBQXdCO0FBQ2hDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsOEVBQXdCO0FBQ2hDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsb0dBQW1DO0FBQzNDLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsNEVBQXVCO0FBQy9CLG1CQUFPLENBQUMsb0VBQW1CO0FBQzNCLG1CQUFPLENBQUMsb0VBQW1CO0FBQzNCLG1CQUFPLENBQUMsOEVBQXdCO0FBQ2hDLG1CQUFPLENBQUMsOEVBQXdCO0FBQ2hDLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsNEZBQStCO0FBQ3ZDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsZ0hBQXlDO0FBQ2pELG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsa0dBQWtDO0FBQzFDLG1CQUFPLENBQUMsb0dBQW1DO0FBQzNDLG1CQUFPLENBQUMsb0dBQW1DO0FBQzNDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsNEdBQXVDO0FBQy9DLG1CQUFPLENBQUMsNEdBQXVDO0FBQy9DLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsb0lBQW1EO0FBQzNELG1CQUFPLENBQUMsOEdBQXdDO0FBQ2hELG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsd0dBQXFDO0FBQzdDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsa0hBQTBDO0FBQ2xELG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsOEdBQXdDO0FBQ2hELG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsZ0ZBQXlCO0FBQ2pDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsd0dBQXFDO0FBQzdDLG1CQUFPLENBQUMsZ0dBQWlDO0FBQ3pDLG1CQUFPLENBQUMsb0lBQW1EO0FBQzNELG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsc0dBQW9DO0FBQzVDLG1CQUFPLENBQUMsc0dBQW9DO0FBQzVDLG1CQUFPLENBQUMsc0dBQW9DO0FBQzVDLG1CQUFPLENBQUMsc0dBQW9DO0FBQzVDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsMEVBQXNCO0FBQzlCLG1CQUFPLENBQUMsMEVBQXNCO0FBQzlCLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsOEVBQXdCO0FBQ2hDLG1CQUFPLENBQUMsOEVBQXdCO0FBQ2hDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsMEVBQXNCO0FBQzlCLG1CQUFPLENBQUMsd0ZBQTZCO0FBQ3JDLG1CQUFPLENBQUMsMEZBQThCO0FBQ3RDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsa0ZBQTBCO0FBQ2xDLG1CQUFPLENBQUMsc0ZBQTRCO0FBQ3BDLG1CQUFPLENBQUMsNEZBQStCO0FBQ3ZDLG1CQUFPLENBQUMsb0ZBQTJCO0FBQ25DLG1CQUFPLENBQUMsNEdBQXVDO0FBQy9DLG1CQUFPLENBQUMsNEdBQXVDO0FBQy9DLG1CQUFPLENBQUMsc0dBQW9DO0FBQzVDLG1CQUFPLENBQUMsZ0hBQXlDO0FBQ2pELG1CQUFPLENBQUMsOEdBQXdDO0FBQ2hELG1CQUFPLENBQUMsd0hBQTZDO0FBQ3JELG1CQUFPLENBQUMsc0dBQW9DO0FBQzVDLG1CQUFPLENBQUMsOEdBQXdDO0FBQ2hELG1CQUFPLENBQUMsOEZBQWdDO0FBQ3hDLG1C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QXpFLENBQTNCOztRQUdJaE4sRyxHQUFBQSxjO1FBQ0ErTSxPLEdBQUFBLGtCO1FBQ0FuUyxJLEdBQUFBLGU7UUFDQXBlLE0sR0FBQUEsaUI7UUFDQW1PLFMsR0FBQUEsb0I7UUFDQWhjLFEsR0FBQUEsbUI7UUFDQXErQixrQixHQUFBQSxrQjs7Ozs7Ozs7Ozs7Ozs7Ozs7a0JDTG9CNWtDLE07O0FBTnhCOzs7Ozs7QUFFQTs7OztBQUllLFNBQVNBLE1BQVQsR0FBa0I7QUFDL0IsU0FBTyxtQkFBUW1hLE9BQVIsQ0FBZ0IsSUFBaEIsRUFBc0IsRUFBdEIsQ0FBUDtBQUNEOzs7Ozs7Ozs7Ozs7Ozs7Ozs7QUNSRCxJQUFNemUsVUFBVSxRQUFoQixDLFFBQWtDQSxPLEdBQUFBLE8iLCJmaWxlIjoib2lkYy1jbGllbnQuanMiLCJzb3VyY2VzQ29udGVudCI6WyIgXHQvLyBUaGUgbW9kdWxlIGNhY2hlXG4gXHR2YXIgaW5zdGFsbGVkTW9kdWxlcyA9IHt9O1xuXG4gXHQvLyBUaGUgcmVxdWlyZSBmdW5jdGlvblxuIFx0ZnVuY3Rpb24gX193ZWJwYWNrX3JlcXVpcmVfXyhtb2R1bGVJZCkge1xuXG4gXHRcdC8vIENoZWNrIGlmIG1vZHVsZSBpcyBpbiBjYWNoZVxuIFx0XHRpZihpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSkge1xuIFx0XHRcdHJldHVybiBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXS5leHBvcnRzO1xuIFx0XHR9XG4gXHRcdC8vIENyZWF0ZSBhIG5ldyBtb2R1bGUgKGFuZCBwdXQgaXQgaW50byB0aGUgY2FjaGUpXG4gXHRcdHZhciBtb2R1bGUgPSBpbnN0YWxsZWRNb2R1bGVzW21vZHVsZUlkXSA9IHtcbiBcdFx0XHRpOiBtb2R1bGVJZCxcbiBcdFx0XHRsOiBmYWxzZSxcbiBcdFx0XHRleHBvcnRzOiB7fVxuIFx0XHR9O1xuXG4gXHRcdC8vIEV4ZWN1dGUgdGhlIG1vZHVsZSBmdW5jdGlvblxuIFx0XHRtb2R1bGVzW21vZHVsZUlkXS5jYWxsKG1vZHVsZS5leHBvcnRzLCBtb2R1bGUsIG1vZHVsZS5leHBvcnRzLCBfX3dlYnBhY2tfcmVxdWlyZV9fKTtcblxuIFx0XHQvLyBGbGFnIHRoZSBtb2R1bGUgYXMgbG9hZGVkXG4gXHRcdG1vZHVsZS5sID0gdHJ1ZTtcblxuIFx0XHQvLyBSZXR1cm4gdGhlIGV4cG9ydHMgb2YgdGhlIG1vZHVsZVxuIFx0XHRyZXR1cm4gbW9kdWxlLmV4cG9ydHM7XG4gXHR9XG5cblxuIFx0Ly8gZXhwb3NlIHRoZSBtb2R1bGVzIG9iamVjdCAoX193ZWJwYWNrX21vZHVsZXNfXylcbiBcdF9fd2VicGFja19yZXF1aXJlX18ubSA9IG1vZHVsZXM7XG5cbiBcdC8vIGV4cG9zZSB0aGUgbW9kdWxlIGNhY2hlXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLmMgPSBpbnN0YWxsZWRNb2R1bGVzO1xuXG4gXHQvLyBkZWZpbmUgZ2V0dGVyIGZ1bmN0aW9uIGZvciBoYXJtb255IGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uZCA9IGZ1bmN0aW9uKGV4cG9ydHMsIG5hbWUsIGdldHRlcikge1xuIFx0XHRpZighX193ZWJwYWNrX3JlcXVpcmVfXy5vKGV4cG9ydHMsIG5hbWUpKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIG5hbWUsIHsgZW51bWVyYWJsZTogdHJ1ZSwgZ2V0OiBnZXR0ZXIgfSk7XG4gXHRcdH1cbiBcdH07XG5cbiBcdC8vIGRlZmluZSBfX2VzTW9kdWxlIG9uIGV4cG9ydHNcbiBcdF9fd2VicGFja19yZXF1aXJlX18uciA9IGZ1bmN0aW9uKGV4cG9ydHMpIHtcbiBcdFx0aWYodHlwZW9mIFN5bWJvbCAhPT0gJ3VuZGVmaW5lZCcgJiYgU3ltYm9sLnRvU3RyaW5nVGFnKSB7XG4gXHRcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsIFN5bWJvbC50b1N0cmluZ1RhZywgeyB2YWx1ZTogJ01vZHVsZScgfSk7XG4gXHRcdH1cbiBcdFx0T2JqZWN0LmRlZmluZVByb3BlcnR5KGV4cG9ydHMsICdfX2VzTW9kdWxlJywgeyB2YWx1ZTogdHJ1ZSB9KTtcbiBcdH07XG5cbiBcdC8vIGNyZWF0ZSBhIGZha2UgbmFtZXNwYWNlIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDE6IHZhbHVlIGlzIGEgbW9kdWxlIGlkLCByZXF1aXJlIGl0XG4gXHQvLyBtb2RlICYgMjogbWVyZ2UgYWxsIHByb3BlcnRpZXMgb2YgdmFsdWUgaW50byB0aGUgbnNcbiBcdC8vIG1vZGUgJiA0OiByZXR1cm4gdmFsdWUgd2hlbiBhbHJlYWR5IG5zIG9iamVjdFxuIFx0Ly8gbW9kZSAmIDh8MTogYmVoYXZlIGxpa2UgcmVxdWlyZVxuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy50ID0gZnVuY3Rpb24odmFsdWUsIG1vZGUpIHtcbiBcdFx0aWYobW9kZSAmIDEpIHZhbHVlID0gX193ZWJwYWNrX3JlcXVpcmVfXyh2YWx1ZSk7XG4gXHRcdGlmKG1vZGUgJiA4KSByZXR1cm4gdmFsdWU7XG4gXHRcdGlmKChtb2RlICYgNCkgJiYgdHlwZW9mIHZhbHVlID09PSAnb2JqZWN0JyAmJiB2YWx1ZSAmJiB2YWx1ZS5fX2VzTW9kdWxlKSByZXR1cm4gdmFsdWU7XG4gXHRcdHZhciBucyA9IE9iamVjdC5jcmVhdGUobnVsbCk7XG4gXHRcdF9fd2VicGFja19yZXF1aXJlX18ucihucyk7XG4gXHRcdE9iamVjdC5kZWZpbmVQcm9wZXJ0eShucywgJ2RlZmF1bHQnLCB7IGVudW1lcmFibGU6IHRydWUsIHZhbHVlOiB2YWx1ZSB9KTtcbiBcdFx0aWYobW9kZSAmIDIgJiYgdHlwZW9mIHZhbHVlICE9ICdzdHJpbmcnKSBmb3IodmFyIGtleSBpbiB2YWx1ZSkgX193ZWJwYWNrX3JlcXVpcmVfXy5kKG5zLCBrZXksIGZ1bmN0aW9uKGtleSkgeyByZXR1cm4gdmFsdWVba2V5XTsgfS5iaW5kKG51bGwsIGtleSkpO1xuIFx0XHRyZXR1cm4gbnM7XG4gXHR9O1xuXG4gXHQvLyBnZXREZWZhdWx0RXhwb3J0IGZ1bmN0aW9uIGZvciBjb21wYXRpYmlsaXR5IHdpdGggbm9uLWhhcm1vbnkgbW9kdWxlc1xuIFx0X193ZWJwYWNrX3JlcXVpcmVfXy5uID0gZnVuY3Rpb24obW9kdWxlKSB7XG4gXHRcdHZhciBnZXR0ZXIgPSBtb2R1bGUgJiYgbW9kdWxlLl9fZXNNb2R1bGUgP1xuIFx0XHRcdGZ1bmN0aW9uIGdldERlZmF1bHQoKSB7IHJldHVybiBtb2R1bGVbJ2RlZmF1bHQnXTsgfSA6XG4gXHRcdFx0ZnVuY3Rpb24gZ2V0TW9kdWxlRXhwb3J0cygpIHsgcmV0dXJuIG1vZHVsZTsgfTtcbiBcdFx0X193ZWJwYWNrX3JlcXVpcmVfXy5kKGdldHRlciwgJ2EnLCBnZXR0ZXIpO1xuIFx0XHRyZXR1cm4gZ2V0dGVyO1xuIFx0fTtcblxuIFx0Ly8gT2JqZWN0LnByb3RvdHlwZS5oYXNPd25Qcm9wZXJ0eS5jYWxsXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLm8gPSBmdW5jdGlvbihvYmplY3QsIHByb3BlcnR5KSB7IHJldHVybiBPYmplY3QucHJvdG90eXBlLmhhc093blByb3BlcnR5LmNhbGwob2JqZWN0LCBwcm9wZXJ0eSk7IH07XG5cbiBcdC8vIF9fd2VicGFja19wdWJsaWNfcGF0aF9fXG4gXHRfX3dlYnBhY2tfcmVxdWlyZV9fLnAgPSBcIlwiO1xuXG5cbiBcdC8vIExvYWQgZW50cnkgbW9kdWxlIGFuZCByZXR1cm4gZXhwb3J0c1xuIFx0cmV0dXJuIF9fd2VicGFja19yZXF1aXJlX18oX193ZWJwYWNrX3JlcXVpcmVfXy5zID0gMCk7XG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL3NyYy9Mb2cuanMnO1xyXG5pbXBvcnQgeyBPaWRjQ2xpZW50IH0gZnJvbSAnLi9zcmMvT2lkY0NsaWVudC5qcyc7XHJcbmltcG9ydCB7IE9pZGNDbGllbnRTZXR0aW5ncyB9IGZyb20gJy4vc3JjL09pZGNDbGllbnRTZXR0aW5ncy5qcyc7XHJcbmltcG9ydCB7IFdlYlN0b3JhZ2VTdGF0ZVN0b3JlIH0gZnJvbSAnLi9zcmMvV2ViU3RvcmFnZVN0YXRlU3RvcmUuanMnO1xyXG5pbXBvcnQgeyBJbk1lbW9yeVdlYlN0b3JhZ2UgfSBmcm9tICcuL3NyYy9Jbk1lbW9yeVdlYlN0b3JhZ2UuanMnO1xyXG5pbXBvcnQgeyBVc2VyTWFuYWdlciB9IGZyb20gJy4vc3JjL1VzZXJNYW5hZ2VyLmpzJztcclxuaW1wb3J0IHsgQWNjZXNzVG9rZW5FdmVudHMgfSBmcm9tICcuL3NyYy9BY2Nlc3NUb2tlbkV2ZW50cy5qcyc7XHJcbmltcG9ydCB7IE1ldGFkYXRhU2VydmljZSB9IGZyb20gJy4vc3JjL01ldGFkYXRhU2VydmljZS5qcyc7XHJcbmltcG9ydCB7IENvcmRvdmFQb3B1cE5hdmlnYXRvciB9IGZyb20gJy4vc3JjL0NvcmRvdmFQb3B1cE5hdmlnYXRvci5qcyc7XHJcbmltcG9ydCB7IENvcmRvdmFJRnJhbWVOYXZpZ2F0b3IgfSBmcm9tICcuL3NyYy9Db3Jkb3ZhSUZyYW1lTmF2aWdhdG9yLmpzJztcclxuaW1wb3J0IHsgQ2hlY2tTZXNzaW9uSUZyYW1lIH0gZnJvbSAnLi9zcmMvQ2hlY2tTZXNzaW9uSUZyYW1lLmpzJztcclxuaW1wb3J0IHsgVG9rZW5SZXZvY2F0aW9uQ2xpZW50IH0gZnJvbSAnLi9zcmMvVG9rZW5SZXZvY2F0aW9uQ2xpZW50LmpzJztcclxuaW1wb3J0IHsgU2Vzc2lvbk1vbml0b3IgfSBmcm9tICcuL3NyYy9TZXNzaW9uTW9uaXRvci5qcyc7XHJcbmltcG9ydCB7IEdsb2JhbCB9IGZyb20gJy4vc3JjL0dsb2JhbC5qcyc7XHJcbmltcG9ydCB7IFVzZXIgfSBmcm9tICcuL3NyYy9Vc2VyLmpzJztcclxuXHJcbmltcG9ydCB7IFZlcnNpb24gfSBmcm9tICcuL3ZlcnNpb24uanMnO1xyXG5cclxuZXhwb3J0IGRlZmF1bHQge1xyXG4gICAgVmVyc2lvbixcclxuICAgIExvZyxcclxuICAgIE9pZGNDbGllbnQsXHJcbiAgICBPaWRjQ2xpZW50U2V0dGluZ3MsXHJcbiAgICBXZWJTdG9yYWdlU3RhdGVTdG9yZSxcclxuICAgIEluTWVtb3J5V2ViU3RvcmFnZSxcclxuICAgIFVzZXJNYW5hZ2VyLFxyXG4gICAgQWNjZXNzVG9rZW5FdmVudHMsXHJcbiAgICBNZXRhZGF0YVNlcnZpY2UsXHJcbiAgICBDb3Jkb3ZhUG9wdXBOYXZpZ2F0b3IsXHJcbiAgICBDb3Jkb3ZhSUZyYW1lTmF2aWdhdG9yLFxyXG4gICAgQ2hlY2tTZXNzaW9uSUZyYW1lLFxyXG4gICAgVG9rZW5SZXZvY2F0aW9uQ2xpZW50LFxyXG4gICAgU2Vzc2lvbk1vbml0b3IsXHJcbiAgICBHbG9iYWwsXHJcbiAgICBVc2VyXHJcbn07XHJcbiIsIi8qXHJcbiAqIGpzcnNhc2lnbihhbGwpIDguMC4xMiAoMjAxOC0wNC0yMikgKGMpIDIwMTAtMjAxOCBLZW5qaSBVcnVzaGltYSB8IGtqdXIuZ2l0aHViLmNvbS9qc3JzYXNpZ24vbGljZW5zZVxyXG4gKi9cclxuXHJcbnZhciBuYXZpZ2F0b3IgPSB7fTtcclxubmF2aWdhdG9yLnVzZXJBZ2VudCA9IGZhbHNlO1xyXG5cclxudmFyIHdpbmRvdyA9IHt9O1xyXG5cbi8qIVxyXG5Db3B5cmlnaHQgKGMpIDIwMTEsIFlhaG9vISBJbmMuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbkNvZGUgbGljZW5zZWQgdW5kZXIgdGhlIEJTRCBMaWNlbnNlOlxyXG5odHRwOi8vZGV2ZWxvcGVyLnlhaG9vLmNvbS95dWkvbGljZW5zZS5odG1sXHJcbnZlcnNpb246IDIuOS4wXHJcbiovXHJcbmlmKFlBSE9PPT09dW5kZWZpbmVkKXt2YXIgWUFIT089e319WUFIT08ubGFuZz17ZXh0ZW5kOmZ1bmN0aW9uKGcsaCxmKXtpZighaHx8IWcpe3Rocm93IG5ldyBFcnJvcihcIllBSE9PLmxhbmcuZXh0ZW5kIGZhaWxlZCwgcGxlYXNlIGNoZWNrIHRoYXQgYWxsIGRlcGVuZGVuY2llcyBhcmUgaW5jbHVkZWQuXCIpfXZhciBkPWZ1bmN0aW9uKCl7fTtkLnByb3RvdHlwZT1oLnByb3RvdHlwZTtnLnByb3RvdHlwZT1uZXcgZCgpO2cucHJvdG90eXBlLmNvbnN0cnVjdG9yPWc7Zy5zdXBlcmNsYXNzPWgucHJvdG90eXBlO2lmKGgucHJvdG90eXBlLmNvbnN0cnVjdG9yPT1PYmplY3QucHJvdG90eXBlLmNvbnN0cnVjdG9yKXtoLnByb3RvdHlwZS5jb25zdHJ1Y3Rvcj1ofWlmKGYpe3ZhciBiO2ZvcihiIGluIGYpe2cucHJvdG90eXBlW2JdPWZbYl19dmFyIGU9ZnVuY3Rpb24oKXt9LGM9W1widG9TdHJpbmdcIixcInZhbHVlT2ZcIl07dHJ5e2lmKC9NU0lFLy50ZXN0KG5hdmlnYXRvci51c2VyQWdlbnQpKXtlPWZ1bmN0aW9uKGosaSl7Zm9yKGI9MDtiPGMubGVuZ3RoO2I9YisxKXt2YXIgbD1jW2JdLGs9aVtsXTtpZih0eXBlb2Ygaz09PVwiZnVuY3Rpb25cIiYmayE9T2JqZWN0LnByb3RvdHlwZVtsXSl7altsXT1rfX19fX1jYXRjaChhKXt9ZShnLnByb3RvdHlwZSxmKX19fTtcbi8qISBDcnlwdG9KUyB2My4xLjIgY29yZS1maXguanNcclxuICogY29kZS5nb29nbGUuY29tL3AvY3J5cHRvLWpzXHJcbiAqIChjKSAyMDA5LTIwMTMgYnkgSmVmZiBNb3R0LiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4gKiBjb2RlLmdvb2dsZS5jb20vcC9jcnlwdG8tanMvd2lraS9MaWNlbnNlXHJcbiAqIFRISVMgSVMgRklYIG9mICdjb3JlLmpzJyB0byBmaXggSG1hYyBpc3N1ZS5cclxuICogaHR0cHM6Ly9jb2RlLmdvb2dsZS5jb20vcC9jcnlwdG8tanMvaXNzdWVzL2RldGFpbD9pZD04NFxyXG4gKiBodHRwczovL2NyeXB0by1qcy5nb29nbGVjb2RlLmNvbS9zdm4taGlzdG9yeS9yNjY3L2JyYW5jaGVzLzMueC9zcmMvY29yZS5qc1xyXG4gKi9cclxudmFyIENyeXB0b0pTPUNyeXB0b0pTfHwoZnVuY3Rpb24oZSxnKXt2YXIgYT17fTt2YXIgYj1hLmxpYj17fTt2YXIgaj1iLkJhc2U9KGZ1bmN0aW9uKCl7ZnVuY3Rpb24gbigpe31yZXR1cm57ZXh0ZW5kOmZ1bmN0aW9uKHApe24ucHJvdG90eXBlPXRoaXM7dmFyIG89bmV3IG4oKTtpZihwKXtvLm1peEluKHApfWlmKCFvLmhhc093blByb3BlcnR5KFwiaW5pdFwiKSl7by5pbml0PWZ1bmN0aW9uKCl7by4kc3VwZXIuaW5pdC5hcHBseSh0aGlzLGFyZ3VtZW50cyl9fW8uaW5pdC5wcm90b3R5cGU9bztvLiRzdXBlcj10aGlzO3JldHVybiBvfSxjcmVhdGU6ZnVuY3Rpb24oKXt2YXIgbz10aGlzLmV4dGVuZCgpO28uaW5pdC5hcHBseShvLGFyZ3VtZW50cyk7cmV0dXJuIG99LGluaXQ6ZnVuY3Rpb24oKXt9LG1peEluOmZ1bmN0aW9uKHApe2Zvcih2YXIgbyBpbiBwKXtpZihwLmhhc093blByb3BlcnR5KG8pKXt0aGlzW29dPXBbb119fWlmKHAuaGFzT3duUHJvcGVydHkoXCJ0b1N0cmluZ1wiKSl7dGhpcy50b1N0cmluZz1wLnRvU3RyaW5nfX0sY2xvbmU6ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5pbml0LnByb3RvdHlwZS5leHRlbmQodGhpcyl9fX0oKSk7dmFyIGw9Yi5Xb3JkQXJyYXk9ai5leHRlbmQoe2luaXQ6ZnVuY3Rpb24obyxuKXtvPXRoaXMud29yZHM9b3x8W107aWYobiE9Zyl7dGhpcy5zaWdCeXRlcz1ufWVsc2V7dGhpcy5zaWdCeXRlcz1vLmxlbmd0aCo0fX0sdG9TdHJpbmc6ZnVuY3Rpb24obil7cmV0dXJuKG58fGgpLnN0cmluZ2lmeSh0aGlzKX0sY29uY2F0OmZ1bmN0aW9uKHQpe3ZhciBxPXRoaXMud29yZHM7dmFyIHA9dC53b3Jkczt2YXIgbj10aGlzLnNpZ0J5dGVzO3ZhciBzPXQuc2lnQnl0ZXM7dGhpcy5jbGFtcCgpO2lmKG4lNCl7Zm9yKHZhciByPTA7cjxzO3IrKyl7dmFyIG89KHBbcj4+PjJdPj4+KDI0LShyJTQpKjgpKSYyNTU7cVsobityKT4+PjJdfD1vPDwoMjQtKChuK3IpJTQpKjgpfX1lbHNle2Zvcih2YXIgcj0wO3I8cztyKz00KXtxWyhuK3IpPj4+Ml09cFtyPj4+Ml19fXRoaXMuc2lnQnl0ZXMrPXM7cmV0dXJuIHRoaXN9LGNsYW1wOmZ1bmN0aW9uKCl7dmFyIG89dGhpcy53b3Jkczt2YXIgbj10aGlzLnNpZ0J5dGVzO29bbj4+PjJdJj00Mjk0OTY3Mjk1PDwoMzItKG4lNCkqOCk7by5sZW5ndGg9ZS5jZWlsKG4vNCl9LGNsb25lOmZ1bmN0aW9uKCl7dmFyIG49ai5jbG9uZS5jYWxsKHRoaXMpO24ud29yZHM9dGhpcy53b3Jkcy5zbGljZSgwKTtyZXR1cm4gbn0scmFuZG9tOmZ1bmN0aW9uKHApe3ZhciBvPVtdO2Zvcih2YXIgbj0wO248cDtuKz00KXtvLnB1c2goKGUucmFuZG9tKCkqNDI5NDk2NzI5Nil8MCl9cmV0dXJuIG5ldyBsLmluaXQobyxwKX19KTt2YXIgbT1hLmVuYz17fTt2YXIgaD1tLkhleD17c3RyaW5naWZ5OmZ1bmN0aW9uKHApe3ZhciByPXAud29yZHM7dmFyIG89cC5zaWdCeXRlczt2YXIgcT1bXTtmb3IodmFyIG49MDtuPG87bisrKXt2YXIgcz0ocltuPj4+Ml0+Pj4oMjQtKG4lNCkqOCkpJjI1NTtxLnB1c2goKHM+Pj40KS50b1N0cmluZygxNikpO3EucHVzaCgocyYxNSkudG9TdHJpbmcoMTYpKX1yZXR1cm4gcS5qb2luKFwiXCIpfSxwYXJzZTpmdW5jdGlvbihwKXt2YXIgbj1wLmxlbmd0aDt2YXIgcT1bXTtmb3IodmFyIG89MDtvPG47bys9Mil7cVtvPj4+M118PXBhcnNlSW50KHAuc3Vic3RyKG8sMiksMTYpPDwoMjQtKG8lOCkqNCl9cmV0dXJuIG5ldyBsLmluaXQocSxuLzIpfX07dmFyIGQ9bS5MYXRpbjE9e3N0cmluZ2lmeTpmdW5jdGlvbihxKXt2YXIgcj1xLndvcmRzO3ZhciBwPXEuc2lnQnl0ZXM7dmFyIG49W107Zm9yKHZhciBvPTA7bzxwO28rKyl7dmFyIHM9KHJbbz4+PjJdPj4+KDI0LShvJTQpKjgpKSYyNTU7bi5wdXNoKFN0cmluZy5mcm9tQ2hhckNvZGUocykpfXJldHVybiBuLmpvaW4oXCJcIil9LHBhcnNlOmZ1bmN0aW9uKHApe3ZhciBuPXAubGVuZ3RoO3ZhciBxPVtdO2Zvcih2YXIgbz0wO288bjtvKyspe3Fbbz4+PjJdfD0ocC5jaGFyQ29kZUF0KG8pJjI1NSk8PCgyNC0obyU0KSo4KX1yZXR1cm4gbmV3IGwuaW5pdChxLG4pfX07dmFyIGM9bS5VdGY4PXtzdHJpbmdpZnk6ZnVuY3Rpb24obil7dHJ5e3JldHVybiBkZWNvZGVVUklDb21wb25lbnQoZXNjYXBlKGQuc3RyaW5naWZ5KG4pKSl9Y2F0Y2gobyl7dGhyb3cgbmV3IEVycm9yKFwiTWFsZm9ybWVkIFVURi04IGRhdGFcIil9fSxwYXJzZTpmdW5jdGlvbihuKXtyZXR1cm4gZC5wYXJzZSh1bmVzY2FwZShlbmNvZGVVUklDb21wb25lbnQobikpKX19O3ZhciBpPWIuQnVmZmVyZWRCbG9ja0FsZ29yaXRobT1qLmV4dGVuZCh7cmVzZXQ6ZnVuY3Rpb24oKXt0aGlzLl9kYXRhPW5ldyBsLmluaXQoKTt0aGlzLl9uRGF0YUJ5dGVzPTB9LF9hcHBlbmQ6ZnVuY3Rpb24obil7aWYodHlwZW9mIG49PVwic3RyaW5nXCIpe249Yy5wYXJzZShuKX10aGlzLl9kYXRhLmNvbmNhdChuKTt0aGlzLl9uRGF0YUJ5dGVzKz1uLnNpZ0J5dGVzfSxfcHJvY2VzczpmdW5jdGlvbih3KXt2YXIgcT10aGlzLl9kYXRhO3ZhciB4PXEud29yZHM7dmFyIG49cS5zaWdCeXRlczt2YXIgdD10aGlzLmJsb2NrU2l6ZTt2YXIgdj10KjQ7dmFyIHU9bi92O2lmKHcpe3U9ZS5jZWlsKHUpfWVsc2V7dT1lLm1heCgodXwwKS10aGlzLl9taW5CdWZmZXJTaXplLDApfXZhciBzPXUqdDt2YXIgcj1lLm1pbihzKjQsbik7aWYocyl7Zm9yKHZhciBwPTA7cDxzO3ArPXQpe3RoaXMuX2RvUHJvY2Vzc0Jsb2NrKHgscCl9dmFyIG89eC5zcGxpY2UoMCxzKTtxLnNpZ0J5dGVzLT1yfXJldHVybiBuZXcgbC5pbml0KG8scil9LGNsb25lOmZ1bmN0aW9uKCl7dmFyIG49ai5jbG9uZS5jYWxsKHRoaXMpO24uX2RhdGE9dGhpcy5fZGF0YS5jbG9uZSgpO3JldHVybiBufSxfbWluQnVmZmVyU2l6ZTowfSk7dmFyIGY9Yi5IYXNoZXI9aS5leHRlbmQoe2NmZzpqLmV4dGVuZCgpLGluaXQ6ZnVuY3Rpb24obil7dGhpcy5jZmc9dGhpcy5jZmcuZXh0ZW5kKG4pO3RoaXMucmVzZXQoKX0scmVzZXQ6ZnVuY3Rpb24oKXtpLnJlc2V0LmNhbGwodGhpcyk7dGhpcy5fZG9SZXNldCgpfSx1cGRhdGU6ZnVuY3Rpb24obil7dGhpcy5fYXBwZW5kKG4pO3RoaXMuX3Byb2Nlc3MoKTtyZXR1cm4gdGhpc30sZmluYWxpemU6ZnVuY3Rpb24obil7aWYobil7dGhpcy5fYXBwZW5kKG4pfXZhciBvPXRoaXMuX2RvRmluYWxpemUoKTtyZXR1cm4gb30sYmxvY2tTaXplOjUxMi8zMixfY3JlYXRlSGVscGVyOmZ1bmN0aW9uKG4pe3JldHVybiBmdW5jdGlvbihwLG8pe3JldHVybiBuZXcgbi5pbml0KG8pLmZpbmFsaXplKHApfX0sX2NyZWF0ZUhtYWNIZWxwZXI6ZnVuY3Rpb24obil7cmV0dXJuIGZ1bmN0aW9uKHAsbyl7cmV0dXJuIG5ldyBrLkhNQUMuaW5pdChuLG8pLmZpbmFsaXplKHApfX19KTt2YXIgaz1hLmFsZ289e307cmV0dXJuIGF9KE1hdGgpKTtcbi8qXHJcbkNyeXB0b0pTIHYzLjEuMiB4NjQtY29yZS1taW4uanNcclxuY29kZS5nb29nbGUuY29tL3AvY3J5cHRvLWpzXHJcbihjKSAyMDA5LTIwMTMgYnkgSmVmZiBNb3R0LiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG5jb2RlLmdvb2dsZS5jb20vcC9jcnlwdG8tanMvd2lraS9MaWNlbnNlXHJcbiovXHJcbihmdW5jdGlvbihnKXt2YXIgYT1DcnlwdG9KUyxmPWEubGliLGU9Zi5CYXNlLGg9Zi5Xb3JkQXJyYXksYT1hLng2ND17fTthLldvcmQ9ZS5leHRlbmQoe2luaXQ6ZnVuY3Rpb24oYixjKXt0aGlzLmhpZ2g9Yjt0aGlzLmxvdz1jfX0pO2EuV29yZEFycmF5PWUuZXh0ZW5kKHtpbml0OmZ1bmN0aW9uKGIsYyl7Yj10aGlzLndvcmRzPWJ8fFtdO3RoaXMuc2lnQnl0ZXM9YyE9Zz9jOjgqYi5sZW5ndGh9LHRvWDMyOmZ1bmN0aW9uKCl7Zm9yKHZhciBiPXRoaXMud29yZHMsYz1iLmxlbmd0aCxhPVtdLGQ9MDtkPGM7ZCsrKXt2YXIgZT1iW2RdO2EucHVzaChlLmhpZ2gpO2EucHVzaChlLmxvdyl9cmV0dXJuIGguY3JlYXRlKGEsdGhpcy5zaWdCeXRlcyl9LGNsb25lOmZ1bmN0aW9uKCl7Zm9yKHZhciBiPWUuY2xvbmUuY2FsbCh0aGlzKSxjPWIud29yZHM9dGhpcy53b3Jkcy5zbGljZSgwKSxhPWMubGVuZ3RoLGQ9MDtkPGE7ZCsrKWNbZF09Y1tkXS5jbG9uZSgpO3JldHVybiBifX0pfSkoKTtcclxuXG4vKlxyXG5DcnlwdG9KUyB2My4xLjIgZW5jLWJhc2U2NC5qc1xyXG5jb2RlLmdvb2dsZS5jb20vcC9jcnlwdG8tanNcclxuKGMpIDIwMDktMjAxMyBieSBKZWZmIE1vdHQuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbmNvZGUuZ29vZ2xlLmNvbS9wL2NyeXB0by1qcy93aWtpL0xpY2Vuc2VcclxuKi9cclxuKGZ1bmN0aW9uKCl7dmFyIGg9Q3J5cHRvSlMsaj1oLmxpYi5Xb3JkQXJyYXk7aC5lbmMuQmFzZTY0PXtzdHJpbmdpZnk6ZnVuY3Rpb24oYil7dmFyIGU9Yi53b3JkcyxmPWIuc2lnQnl0ZXMsYz10aGlzLl9tYXA7Yi5jbGFtcCgpO2I9W107Zm9yKHZhciBhPTA7YTxmO2ErPTMpZm9yKHZhciBkPShlW2E+Pj4yXT4+PjI0LTgqKGElNCkmMjU1KTw8MTZ8KGVbYSsxPj4+Ml0+Pj4yNC04KigoYSsxKSU0KSYyNTUpPDw4fGVbYSsyPj4+Ml0+Pj4yNC04KigoYSsyKSU0KSYyNTUsZz0wOzQ+ZyYmYSswLjc1Kmc8ZjtnKyspYi5wdXNoKGMuY2hhckF0KGQ+Pj42KigzLWcpJjYzKSk7aWYoZT1jLmNoYXJBdCg2NCkpZm9yKDtiLmxlbmd0aCU0OyliLnB1c2goZSk7cmV0dXJuIGIuam9pbihcIlwiKX0scGFyc2U6ZnVuY3Rpb24oYil7dmFyIGU9Yi5sZW5ndGgsZj10aGlzLl9tYXAsYz1mLmNoYXJBdCg2NCk7YyYmKGM9Yi5pbmRleE9mKGMpLC0xIT1jJiYoZT1jKSk7Zm9yKHZhciBjPVtdLGE9MCxkPTA7ZDxcclxuZTtkKyspaWYoZCU0KXt2YXIgZz1mLmluZGV4T2YoYi5jaGFyQXQoZC0xKSk8PDIqKGQlNCksaD1mLmluZGV4T2YoYi5jaGFyQXQoZCkpPj4+Ni0yKihkJTQpO2NbYT4+PjJdfD0oZ3xoKTw8MjQtOCooYSU0KTthKyt9cmV0dXJuIGouY3JlYXRlKGMsYSl9LF9tYXA6XCJBQkNERUZHSElKS0xNTk9QUVJTVFVWV1hZWmFiY2RlZmdoaWprbG1ub3BxcnN0dXZ3eHl6MDEyMzQ1Njc4OSsvPVwifX0pKCk7XHJcblxuLypcclxuQ3J5cHRvSlMgdjMuMS4yIHNoYTI1Ni1taW4uanNcclxuY29kZS5nb29nbGUuY29tL3AvY3J5cHRvLWpzXHJcbihjKSAyMDA5LTIwMTMgYnkgSmVmZiBNb3R0LiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG5jb2RlLmdvb2dsZS5jb20vcC9jcnlwdG8tanMvd2lraS9MaWNlbnNlXHJcbiovXHJcbihmdW5jdGlvbihrKXtmb3IodmFyIGc9Q3J5cHRvSlMsaD1nLmxpYix2PWguV29yZEFycmF5LGo9aC5IYXNoZXIsaD1nLmFsZ28scz1bXSx0PVtdLHU9ZnVuY3Rpb24ocSl7cmV0dXJuIDQyOTQ5NjcyOTYqKHEtKHF8MCkpfDB9LGw9MixiPTA7NjQ+Yjspe3ZhciBkO2E6e2Q9bDtmb3IodmFyIHc9ay5zcXJ0KGQpLHI9MjtyPD13O3IrKylpZighKGQlcikpe2Q9ITE7YnJlYWsgYX1kPSEwfWQmJig4PmImJihzW2JdPXUoay5wb3cobCwwLjUpKSksdFtiXT11KGsucG93KGwsMS8zKSksYisrKTtsKyt9dmFyIG49W10saD1oLlNIQTI1Nj1qLmV4dGVuZCh7X2RvUmVzZXQ6ZnVuY3Rpb24oKXt0aGlzLl9oYXNoPW5ldyB2LmluaXQocy5zbGljZSgwKSl9LF9kb1Byb2Nlc3NCbG9jazpmdW5jdGlvbihxLGgpe2Zvcih2YXIgYT10aGlzLl9oYXNoLndvcmRzLGM9YVswXSxkPWFbMV0sYj1hWzJdLGs9YVszXSxmPWFbNF0sZz1hWzVdLGo9YVs2XSxsPWFbN10sZT0wOzY0PmU7ZSsrKXtpZigxNj5lKW5bZV09XHJcbnFbaCtlXXwwO2Vsc2V7dmFyIG09bltlLTE1XSxwPW5bZS0yXTtuW2VdPSgobTw8MjV8bT4+PjcpXihtPDwxNHxtPj4+MTgpXm0+Pj4zKStuW2UtN10rKChwPDwxNXxwPj4+MTcpXihwPDwxM3xwPj4+MTkpXnA+Pj4xMCkrbltlLTE2XX1tPWwrKChmPDwyNnxmPj4+NileKGY8PDIxfGY+Pj4xMSleKGY8PDd8Zj4+PjI1KSkrKGYmZ15+ZiZqKSt0W2VdK25bZV07cD0oKGM8PDMwfGM+Pj4yKV4oYzw8MTl8Yz4+PjEzKV4oYzw8MTB8Yz4+PjIyKSkrKGMmZF5jJmJeZCZiKTtsPWo7aj1nO2c9ZjtmPWsrbXwwO2s9YjtiPWQ7ZD1jO2M9bStwfDB9YVswXT1hWzBdK2N8MDthWzFdPWFbMV0rZHwwO2FbMl09YVsyXStifDA7YVszXT1hWzNdK2t8MDthWzRdPWFbNF0rZnwwO2FbNV09YVs1XStnfDA7YVs2XT1hWzZdK2p8MDthWzddPWFbN10rbHwwfSxfZG9GaW5hbGl6ZTpmdW5jdGlvbigpe3ZhciBkPXRoaXMuX2RhdGEsYj1kLndvcmRzLGE9OCp0aGlzLl9uRGF0YUJ5dGVzLGM9OCpkLnNpZ0J5dGVzO1xyXG5iW2M+Pj41XXw9MTI4PDwyNC1jJTMyO2JbKGMrNjQ+Pj45PDw0KSsxNF09ay5mbG9vcihhLzQyOTQ5NjcyOTYpO2JbKGMrNjQ+Pj45PDw0KSsxNV09YTtkLnNpZ0J5dGVzPTQqYi5sZW5ndGg7dGhpcy5fcHJvY2VzcygpO3JldHVybiB0aGlzLl9oYXNofSxjbG9uZTpmdW5jdGlvbigpe3ZhciBiPWouY2xvbmUuY2FsbCh0aGlzKTtiLl9oYXNoPXRoaXMuX2hhc2guY2xvbmUoKTtyZXR1cm4gYn19KTtnLlNIQTI1Nj1qLl9jcmVhdGVIZWxwZXIoaCk7Zy5IbWFjU0hBMjU2PWouX2NyZWF0ZUhtYWNIZWxwZXIoaCl9KShNYXRoKTtcclxuXG4vKlxyXG5DcnlwdG9KUyB2My4xLjIgc2hhNTEyLW1pbi5qc1xyXG5jb2RlLmdvb2dsZS5jb20vcC9jcnlwdG8tanNcclxuKGMpIDIwMDktMjAxMyBieSBKZWZmIE1vdHQuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbmNvZGUuZ29vZ2xlLmNvbS9wL2NyeXB0by1qcy93aWtpL0xpY2Vuc2VcclxuKi9cclxuKGZ1bmN0aW9uKCl7ZnVuY3Rpb24gYSgpe3JldHVybiBkLmNyZWF0ZS5hcHBseShkLGFyZ3VtZW50cyl9Zm9yKHZhciBuPUNyeXB0b0pTLHI9bi5saWIuSGFzaGVyLGU9bi54NjQsZD1lLldvcmQsVD1lLldvcmRBcnJheSxlPW4uYWxnbyxlYT1bYSgxMTE2MzUyNDA4LDM2MDk3Njc0NTgpLGEoMTg5OTQ0NzQ0MSw2MDI4OTE3MjUpLGEoMzA0OTMyMzQ3MSwzOTY0NDg0Mzk5KSxhKDM5MjEwMDk1NzMsMjE3MzI5NTU0OCksYSg5NjE5ODcxNjMsNDA4MTYyODQ3MiksYSgxNTA4OTcwOTkzLDMwNTM4MzQyNjUpLGEoMjQ1MzYzNTc0OCwyOTM3NjcxNTc5KSxhKDI4NzA3NjMyMjEsMzY2NDYwOTU2MCksYSgzNjI0MzgxMDgwLDI3MzQ4ODMzOTQpLGEoMzEwNTk4NDAxLDExNjQ5OTY1NDIpLGEoNjA3MjI1Mjc4LDEzMjM2MTA3NjQpLGEoMTQyNjg4MTk4NywzNTkwMzA0OTk0KSxhKDE5MjUwNzgzODgsNDA2ODE4MjM4MyksYSgyMTYyMDc4MjA2LDk5MTMzNjExMyksYSgyNjE0ODg4MTAzLDYzMzgwMzMxNyksXHJcbmEoMzI0ODIyMjU4MCwzNDc5Nzc0ODY4KSxhKDM4MzUzOTA0MDEsMjY2NjYxMzQ1OCksYSg0MDIyMjI0Nzc0LDk0NDcxMTEzOSksYSgyNjQzNDcwNzgsMjM0MTI2Mjc3MyksYSg2MDQ4MDc2MjgsMjAwNzgwMDkzMyksYSg3NzAyNTU5ODMsMTQ5NTk5MDkwMSksYSgxMjQ5MTUwMTIyLDE4NTY0MzEyMzUpLGEoMTU1NTA4MTY5MiwzMTc1MjE4MTMyKSxhKDE5OTYwNjQ5ODYsMjE5ODk1MDgzNyksYSgyNTU0MjIwODgyLDM5OTk3MTkzMzkpLGEoMjgyMTgzNDM0OSw3NjY3ODQwMTYpLGEoMjk1Mjk5NjgwOCwyNTY2NTk0ODc5KSxhKDMyMTAzMTM2NzEsMzIwMzMzNzk1NiksYSgzMzM2NTcxODkxLDEwMzQ0NTcwMjYpLGEoMzU4NDUyODcxMSwyNDY2OTQ4OTAxKSxhKDExMzkyNjk5MywzNzU4MzI2MzgzKSxhKDMzODI0MTg5NSwxNjg3MTc5MzYpLGEoNjY2MzA3MjA1LDExODgxNzk5NjQpLGEoNzczNTI5OTEyLDE1NDYwNDU3MzQpLGEoMTI5NDc1NzM3MiwxNTIyODA1NDg1KSxhKDEzOTYxODIyOTEsXHJcbjI2NDM4MzM4MjMpLGEoMTY5NTE4MzcwMCwyMzQzNTI3MzkwKSxhKDE5ODY2NjEwNTEsMTAxNDQ3NzQ4MCksYSgyMTc3MDI2MzUwLDEyMDY3NTkxNDIpLGEoMjQ1Njk1NjAzNywzNDQwNzc2MjcpLGEoMjczMDQ4NTkyMSwxMjkwODYzNDYwKSxhKDI4MjAzMDI0MTEsMzE1ODQ1NDI3MyksYSgzMjU5NzMwODAwLDM1MDU5NTI2NTcpLGEoMzM0NTc2NDc3MSwxMDYyMTcwMDgpLGEoMzUxNjA2NTgxNywzNjA2MDA4MzQ0KSxhKDM2MDAzNTI4MDQsMTQzMjcyNTc3NiksYSg0MDk0NTcxOTA5LDE0NjcwMzE1OTQpLGEoMjc1NDIzMzQ0LDg1MTE2OTcyMCksYSg0MzAyMjc3MzQsMzEwMDgyMzc1MiksYSg1MDY5NDg2MTYsMTM2MzI1ODE5NSksYSg2NTkwNjA1NTYsMzc1MDY4NTU5MyksYSg4ODM5OTc4NzcsMzc4NTA1MDI4MCksYSg5NTgxMzk1NzEsMzMxODMwNzQyNyksYSgxMzIyODIyMjE4LDM4MTI3MjM0MDMpLGEoMTUzNzAwMjA2MywyMDAzMDM0OTk1KSxhKDE3NDc4NzM3NzksMzYwMjAzNjg5OSksXHJcbmEoMTk1NTU2MjIyMiwxNTc1OTkwMDEyKSxhKDIwMjQxMDQ4MTUsMTEyNTU5MjkyOCksYSgyMjI3NzMwNDUyLDI3MTY5MDQzMDYpLGEoMjM2MTg1MjQyNCw0NDI3NzYwNDQpLGEoMjQyODQzNjQ3NCw1OTM2OTgzNDQpLGEoMjc1NjczNDE4NywzNzMzMTEwMjQ5KSxhKDMyMDQwMzE0NzksMjk5OTM1MTU3MyksYSgzMzI5MzI1Mjk4LDM4MTU5MjA0MjcpLGEoMzM5MTU2OTYxNCwzOTI4MzgzOTAwKSxhKDM1MTUyNjcyNzEsNTY2MjgwNzExKSxhKDM5NDAxODc2MDYsMzQ1NDA2OTUzNCksYSg0MTE4NjMwMjcxLDQwMDAyMzk5OTIpLGEoMTE2NDE4NDc0LDE5MTQxMzg1NTQpLGEoMTc0MjkyNDIxLDI3MzEwNTUyNzApLGEoMjg5MzgwMzU2LDMyMDM5OTMwMDYpLGEoNDYwMzkzMjY5LDMyMDYyMDMxNSksYSg2ODU0NzE3MzMsNTg3NDk2ODM2KSxhKDg1MjE0Mjk3MSwxMDg2NzkyODUxKSxhKDEwMTcwMzYyOTgsMzY1NTQzMTAwKSxhKDExMjYwMDA1ODAsMjYxODI5NzY3NiksYSgxMjg4MDMzNDcwLFxyXG4zNDA5ODU1MTU4KSxhKDE1MDE1MDU5NDgsNDIzNDUwOTg2NiksYSgxNjA3MTY3OTE1LDk4NzE2NzQ2OCksYSgxODE2NDAyMzE2LDEyNDYxODk1OTEpXSx2PVtdLHc9MDs4MD53O3crKyl2W3ddPWEoKTtlPWUuU0hBNTEyPXIuZXh0ZW5kKHtfZG9SZXNldDpmdW5jdGlvbigpe3RoaXMuX2hhc2g9bmV3IFQuaW5pdChbbmV3IGQuaW5pdCgxNzc5MDMzNzAzLDQwODkyMzU3MjApLG5ldyBkLmluaXQoMzE0NDEzNDI3NywyMjI3ODczNTk1KSxuZXcgZC5pbml0KDEwMTM5MDQyNDIsNDI3MTE3NTcyMyksbmV3IGQuaW5pdCgyNzczNDgwNzYyLDE1OTU3NTAxMjkpLG5ldyBkLmluaXQoMTM1OTg5MzExOSwyOTE3NTY1MTM3KSxuZXcgZC5pbml0KDI2MDA4MjI5MjQsNzI1NTExMTk5KSxuZXcgZC5pbml0KDUyODczNDYzNSw0MjE1Mzg5NTQ3KSxuZXcgZC5pbml0KDE1NDE0NTkyMjUsMzI3MDMzMjA5KV0pfSxfZG9Qcm9jZXNzQmxvY2s6ZnVuY3Rpb24oYSxkKXtmb3IodmFyIGY9dGhpcy5faGFzaC53b3JkcyxcclxuRj1mWzBdLGU9ZlsxXSxuPWZbMl0scj1mWzNdLEc9Zls0XSxIPWZbNV0sST1mWzZdLGY9Zls3XSx3PUYuaGlnaCxKPUYubG93LFg9ZS5oaWdoLEs9ZS5sb3csWT1uLmhpZ2gsTD1uLmxvdyxaPXIuaGlnaCxNPXIubG93LCQ9Ry5oaWdoLE49Ry5sb3csYWE9SC5oaWdoLE89SC5sb3csYmE9SS5oaWdoLFA9SS5sb3csY2E9Zi5oaWdoLFE9Zi5sb3csaz13LGc9Six6PVgseD1LLEE9WSx5PUwsVT1aLEI9TSxsPSQsaD1OLFI9YWEsQz1PLFM9YmEsRD1QLFY9Y2EsRT1RLG09MDs4MD5tO20rKyl7dmFyIHM9dlttXTtpZigxNj5tKXZhciBqPXMuaGlnaD1hW2QrMiptXXwwLGI9cy5sb3c9YVtkKzIqbSsxXXwwO2Vsc2V7dmFyIGo9dlttLTE1XSxiPWouaGlnaCxwPWoubG93LGo9KGI+Pj4xfHA8PDMxKV4oYj4+Pjh8cDw8MjQpXmI+Pj43LHA9KHA+Pj4xfGI8PDMxKV4ocD4+Pjh8Yjw8MjQpXihwPj4+N3xiPDwyNSksdT12W20tMl0sYj11LmhpZ2gsYz11Lmxvdyx1PShiPj4+MTl8Yzw8MTMpXihiPDxcclxuM3xjPj4+MjkpXmI+Pj42LGM9KGM+Pj4xOXxiPDwxMyleKGM8PDN8Yj4+PjI5KV4oYz4+PjZ8Yjw8MjYpLGI9dlttLTddLFc9Yi5oaWdoLHQ9dlttLTE2XSxxPXQuaGlnaCx0PXQubG93LGI9cCtiLmxvdyxqPWorVysoYj4+PjA8cD4+PjA/MTowKSxiPWIrYyxqPWordSsoYj4+PjA8Yz4+PjA/MTowKSxiPWIrdCxqPWorcSsoYj4+PjA8dD4+PjA/MTowKTtzLmhpZ2g9ajtzLmxvdz1ifXZhciBXPWwmUl5+bCZTLHQ9aCZDXn5oJkQscz1rJnpeayZBXnomQSxUPWcmeF5nJnleeCZ5LHA9KGs+Pj4yOHxnPDw0KV4oazw8MzB8Zz4+PjIpXihrPDwyNXxnPj4+NyksdT0oZz4+PjI4fGs8PDQpXihnPDwzMHxrPj4+MileKGc8PDI1fGs+Pj43KSxjPWVhW21dLGZhPWMuaGlnaCxkYT1jLmxvdyxjPUUrKChoPj4+MTR8bDw8MTgpXihoPj4+MTh8bDw8MTQpXihoPDwyM3xsPj4+OSkpLHE9VisoKGw+Pj4xNHxoPDwxOCleKGw+Pj4xOHxoPDwxNCleKGw8PDIzfGg+Pj45KSkrKGM+Pj4wPEU+Pj4wPzE6XHJcbjApLGM9Yyt0LHE9cStXKyhjPj4+MDx0Pj4+MD8xOjApLGM9YytkYSxxPXErZmErKGM+Pj4wPGRhPj4+MD8xOjApLGM9YytiLHE9cStqKyhjPj4+MDxiPj4+MD8xOjApLGI9dStULHM9cCtzKyhiPj4+MDx1Pj4+MD8xOjApLFY9UyxFPUQsUz1SLEQ9QyxSPWwsQz1oLGg9QitjfDAsbD1VK3ErKGg+Pj4wPEI+Pj4wPzE6MCl8MCxVPUEsQj15LEE9eix5PXgsej1rLHg9ZyxnPWMrYnwwLGs9cStzKyhnPj4+MDxjPj4+MD8xOjApfDB9Sj1GLmxvdz1KK2c7Ri5oaWdoPXcraysoSj4+PjA8Zz4+PjA/MTowKTtLPWUubG93PUsreDtlLmhpZ2g9WCt6KyhLPj4+MDx4Pj4+MD8xOjApO0w9bi5sb3c9TCt5O24uaGlnaD1ZK0ErKEw+Pj4wPHk+Pj4wPzE6MCk7TT1yLmxvdz1NK0I7ci5oaWdoPVorVSsoTT4+PjA8Qj4+PjA/MTowKTtOPUcubG93PU4raDtHLmhpZ2g9JCtsKyhOPj4+MDxoPj4+MD8xOjApO089SC5sb3c9TytDO0guaGlnaD1hYStSKyhPPj4+MDxDPj4+MD8xOjApO1A9SS5sb3c9UCtEO1xyXG5JLmhpZ2g9YmErUysoUD4+PjA8RD4+PjA/MTowKTtRPWYubG93PVErRTtmLmhpZ2g9Y2ErVisoUT4+PjA8RT4+PjA/MTowKX0sX2RvRmluYWxpemU6ZnVuY3Rpb24oKXt2YXIgYT10aGlzLl9kYXRhLGQ9YS53b3JkcyxmPTgqdGhpcy5fbkRhdGFCeXRlcyxlPTgqYS5zaWdCeXRlcztkW2U+Pj41XXw9MTI4PDwyNC1lJTMyO2RbKGUrMTI4Pj4+MTA8PDUpKzMwXT1NYXRoLmZsb29yKGYvNDI5NDk2NzI5Nik7ZFsoZSsxMjg+Pj4xMDw8NSkrMzFdPWY7YS5zaWdCeXRlcz00KmQubGVuZ3RoO3RoaXMuX3Byb2Nlc3MoKTtyZXR1cm4gdGhpcy5faGFzaC50b1gzMigpfSxjbG9uZTpmdW5jdGlvbigpe3ZhciBhPXIuY2xvbmUuY2FsbCh0aGlzKTthLl9oYXNoPXRoaXMuX2hhc2guY2xvbmUoKTtyZXR1cm4gYX0sYmxvY2tTaXplOjMyfSk7bi5TSEE1MTI9ci5fY3JlYXRlSGVscGVyKGUpO24uSG1hY1NIQTUxMj1yLl9jcmVhdGVIbWFjSGVscGVyKGUpfSkoKTtcclxuXG4vKlxyXG5DcnlwdG9KUyB2My4xLjIgc2hhMzg0LW1pbi5qc1xyXG5jb2RlLmdvb2dsZS5jb20vcC9jcnlwdG8tanNcclxuKGMpIDIwMDktMjAxMyBieSBKZWZmIE1vdHQuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbmNvZGUuZ29vZ2xlLmNvbS9wL2NyeXB0by1qcy93aWtpL0xpY2Vuc2VcclxuKi9cclxuKGZ1bmN0aW9uKCl7dmFyIGM9Q3J5cHRvSlMsYT1jLng2NCxiPWEuV29yZCxlPWEuV29yZEFycmF5LGE9Yy5hbGdvLGQ9YS5TSEE1MTIsYT1hLlNIQTM4ND1kLmV4dGVuZCh7X2RvUmVzZXQ6ZnVuY3Rpb24oKXt0aGlzLl9oYXNoPW5ldyBlLmluaXQoW25ldyBiLmluaXQoMzQxODA3MDM2NSwzMjM4MzcxMDMyKSxuZXcgYi5pbml0KDE2NTQyNzAyNTAsOTE0MTUwNjYzKSxuZXcgYi5pbml0KDI0Mzg1MjkzNzAsODEyNzAyOTk5KSxuZXcgYi5pbml0KDM1NTQ2MjM2MCw0MTQ0OTEyNjk3KSxuZXcgYi5pbml0KDE3MzE0MDU0MTUsNDI5MDc3NTg1NyksbmV3IGIuaW5pdCgyMzk0MTgwMjMxLDE3NTA2MDMwMjUpLG5ldyBiLmluaXQoMzY3NTAwODUyNSwxNjk0MDc2ODM5KSxuZXcgYi5pbml0KDEyMDMwNjI4MTMsMzIwNDA3NTQyOCldKX0sX2RvRmluYWxpemU6ZnVuY3Rpb24oKXt2YXIgYT1kLl9kb0ZpbmFsaXplLmNhbGwodGhpcyk7YS5zaWdCeXRlcy09MTY7cmV0dXJuIGF9fSk7Yy5TSEEzODQ9XHJcbmQuX2NyZWF0ZUhlbHBlcihhKTtjLkhtYWNTSEEzODQ9ZC5fY3JlYXRlSG1hY0hlbHBlcihhKX0pKCk7XHJcblxuLyohIChjKSBUb20gV3UgfCBodHRwOi8vd3d3LWNzLXN0dWRlbnRzLnN0YW5mb3JkLmVkdS9+dGp3L2pzYm4vXHJcbiAqL1xyXG52YXIgYjY0bWFwPVwiQUJDREVGR0hJSktMTU5PUFFSU1RVVldYWVphYmNkZWZnaGlqa2xtbm9wcXJzdHV2d3h5ejAxMjM0NTY3ODkrL1wiO3ZhciBiNjRwYWQ9XCI9XCI7ZnVuY3Rpb24gaGV4MmI2NChkKXt2YXIgYjt2YXIgZTt2YXIgYT1cIlwiO2ZvcihiPTA7YiszPD1kLmxlbmd0aDtiKz0zKXtlPXBhcnNlSW50KGQuc3Vic3RyaW5nKGIsYiszKSwxNik7YSs9YjY0bWFwLmNoYXJBdChlPj42KStiNjRtYXAuY2hhckF0KGUmNjMpfWlmKGIrMT09ZC5sZW5ndGgpe2U9cGFyc2VJbnQoZC5zdWJzdHJpbmcoYixiKzEpLDE2KTthKz1iNjRtYXAuY2hhckF0KGU8PDIpfWVsc2V7aWYoYisyPT1kLmxlbmd0aCl7ZT1wYXJzZUludChkLnN1YnN0cmluZyhiLGIrMiksMTYpO2ErPWI2NG1hcC5jaGFyQXQoZT4+MikrYjY0bWFwLmNoYXJBdCgoZSYzKTw8NCl9fWlmKGI2NHBhZCl7d2hpbGUoKGEubGVuZ3RoJjMpPjApe2ErPWI2NHBhZH19cmV0dXJuIGF9ZnVuY3Rpb24gYjY0dG9oZXgoZil7dmFyIGQ9XCJcIjt2YXIgZTt2YXIgYj0wO3ZhciBjO3ZhciBhO2ZvcihlPTA7ZTxmLmxlbmd0aDsrK2Upe2lmKGYuY2hhckF0KGUpPT1iNjRwYWQpe2JyZWFrfWE9YjY0bWFwLmluZGV4T2YoZi5jaGFyQXQoZSkpO2lmKGE8MCl7Y29udGludWV9aWYoYj09MCl7ZCs9aW50MmNoYXIoYT4+Mik7Yz1hJjM7Yj0xfWVsc2V7aWYoYj09MSl7ZCs9aW50MmNoYXIoKGM8PDIpfChhPj40KSk7Yz1hJjE1O2I9Mn1lbHNle2lmKGI9PTIpe2QrPWludDJjaGFyKGMpO2QrPWludDJjaGFyKGE+PjIpO2M9YSYzO2I9M31lbHNle2QrPWludDJjaGFyKChjPDwyKXwoYT4+NCkpO2QrPWludDJjaGFyKGEmMTUpO2I9MH19fX1pZihiPT0xKXtkKz1pbnQyY2hhcihjPDwyKX1yZXR1cm4gZH1mdW5jdGlvbiBiNjR0b0JBKGUpe3ZhciBkPWI2NHRvaGV4KGUpO3ZhciBjO3ZhciBiPW5ldyBBcnJheSgpO2ZvcihjPTA7MipjPGQubGVuZ3RoOysrYyl7YltjXT1wYXJzZUludChkLnN1YnN0cmluZygyKmMsMipjKzIpLDE2KX1yZXR1cm4gYn07XG4vKiEgKGMpIFRvbSBXdSB8IGh0dHA6Ly93d3ctY3Mtc3R1ZGVudHMuc3RhbmZvcmQuZWR1L350ancvanNibi9cclxuICovXHJcbnZhciBkYml0czt2YXIgY2FuYXJ5PTI0NDgzNzgxNDA5NDU5MDt2YXIgal9sbT0oKGNhbmFyeSYxNjc3NzIxNSk9PTE1NzE1MDcwKTtmdW5jdGlvbiBCaWdJbnRlZ2VyKGUsZCxmKXtpZihlIT1udWxsKXtpZihcIm51bWJlclwiPT10eXBlb2YgZSl7dGhpcy5mcm9tTnVtYmVyKGUsZCxmKX1lbHNle2lmKGQ9PW51bGwmJlwic3RyaW5nXCIhPXR5cGVvZiBlKXt0aGlzLmZyb21TdHJpbmcoZSwyNTYpfWVsc2V7dGhpcy5mcm9tU3RyaW5nKGUsZCl9fX19ZnVuY3Rpb24gbmJpKCl7cmV0dXJuIG5ldyBCaWdJbnRlZ2VyKG51bGwpfWZ1bmN0aW9uIGFtMShmLGEsYixlLGgsZyl7d2hpbGUoLS1nPj0wKXt2YXIgZD1hKnRoaXNbZisrXStiW2VdK2g7aD1NYXRoLmZsb29yKGQvNjcxMDg4NjQpO2JbZSsrXT1kJjY3MTA4ODYzfXJldHVybiBofWZ1bmN0aW9uIGFtMihmLHEscixlLG8sYSl7dmFyIGs9cSYzMjc2NyxwPXE+PjE1O3doaWxlKC0tYT49MCl7dmFyIGQ9dGhpc1tmXSYzMjc2Nzt2YXIgZz10aGlzW2YrK10+PjE1O3ZhciBiPXAqZCtnKms7ZD1rKmQrKChiJjMyNzY3KTw8MTUpK3JbZV0rKG8mMTA3Mzc0MTgyMyk7bz0oZD4+PjMwKSsoYj4+PjE1KStwKmcrKG8+Pj4zMCk7cltlKytdPWQmMTA3Mzc0MTgyM31yZXR1cm4gb31mdW5jdGlvbiBhbTMoZixxLHIsZSxvLGEpe3ZhciBrPXEmMTYzODMscD1xPj4xNDt3aGlsZSgtLWE+PTApe3ZhciBkPXRoaXNbZl0mMTYzODM7dmFyIGc9dGhpc1tmKytdPj4xNDt2YXIgYj1wKmQrZyprO2Q9aypkKygoYiYxNjM4Myk8PDE0KStyW2VdK287bz0oZD4+MjgpKyhiPj4xNCkrcCpnO3JbZSsrXT1kJjI2ODQzNTQ1NX1yZXR1cm4gb31pZihqX2xtJiYobmF2aWdhdG9yLmFwcE5hbWU9PVwiTWljcm9zb2Z0IEludGVybmV0IEV4cGxvcmVyXCIpKXtCaWdJbnRlZ2VyLnByb3RvdHlwZS5hbT1hbTI7ZGJpdHM9MzB9ZWxzZXtpZihqX2xtJiYobmF2aWdhdG9yLmFwcE5hbWUhPVwiTmV0c2NhcGVcIikpe0JpZ0ludGVnZXIucHJvdG90eXBlLmFtPWFtMTtkYml0cz0yNn1lbHNle0JpZ0ludGVnZXIucHJvdG90eXBlLmFtPWFtMztkYml0cz0yOH19QmlnSW50ZWdlci5wcm90b3R5cGUuREI9ZGJpdHM7QmlnSW50ZWdlci5wcm90b3R5cGUuRE09KCgxPDxkYml0cyktMSk7QmlnSW50ZWdlci5wcm90b3R5cGUuRFY9KDE8PGRiaXRzKTt2YXIgQklfRlA9NTI7QmlnSW50ZWdlci5wcm90b3R5cGUuRlY9TWF0aC5wb3coMixCSV9GUCk7QmlnSW50ZWdlci5wcm90b3R5cGUuRjE9QklfRlAtZGJpdHM7QmlnSW50ZWdlci5wcm90b3R5cGUuRjI9MipkYml0cy1CSV9GUDt2YXIgQklfUk09XCIwMTIzNDU2Nzg5YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXpcIjt2YXIgQklfUkM9bmV3IEFycmF5KCk7dmFyIHJyLHZ2O3JyPVwiMFwiLmNoYXJDb2RlQXQoMCk7Zm9yKHZ2PTA7dnY8PTk7Kyt2dil7QklfUkNbcnIrK109dnZ9cnI9XCJhXCIuY2hhckNvZGVBdCgwKTtmb3IodnY9MTA7dnY8MzY7Kyt2dil7QklfUkNbcnIrK109dnZ9cnI9XCJBXCIuY2hhckNvZGVBdCgwKTtmb3IodnY9MTA7dnY8MzY7Kyt2dil7QklfUkNbcnIrK109dnZ9ZnVuY3Rpb24gaW50MmNoYXIoYSl7cmV0dXJuIEJJX1JNLmNoYXJBdChhKX1mdW5jdGlvbiBpbnRBdChiLGEpe3ZhciBkPUJJX1JDW2IuY2hhckNvZGVBdChhKV07cmV0dXJuKGQ9PW51bGwpPy0xOmR9ZnVuY3Rpb24gYm5wQ29weVRvKGIpe2Zvcih2YXIgYT10aGlzLnQtMTthPj0wOy0tYSl7YlthXT10aGlzW2FdfWIudD10aGlzLnQ7Yi5zPXRoaXMuc31mdW5jdGlvbiBibnBGcm9tSW50KGEpe3RoaXMudD0xO3RoaXMucz0oYTwwKT8tMTowO2lmKGE+MCl7dGhpc1swXT1hfWVsc2V7aWYoYTwtMSl7dGhpc1swXT1hK3RoaXMuRFZ9ZWxzZXt0aGlzLnQ9MH19fWZ1bmN0aW9uIG5idihhKXt2YXIgYj1uYmkoKTtiLmZyb21JbnQoYSk7cmV0dXJuIGJ9ZnVuY3Rpb24gYm5wRnJvbVN0cmluZyhoLGMpe3ZhciBlO2lmKGM9PTE2KXtlPTR9ZWxzZXtpZihjPT04KXtlPTN9ZWxzZXtpZihjPT0yNTYpe2U9OH1lbHNle2lmKGM9PTIpe2U9MX1lbHNle2lmKGM9PTMyKXtlPTV9ZWxzZXtpZihjPT00KXtlPTJ9ZWxzZXt0aGlzLmZyb21SYWRpeChoLGMpO3JldHVybn19fX19fXRoaXMudD0wO3RoaXMucz0wO3ZhciBnPWgubGVuZ3RoLGQ9ZmFsc2UsZj0wO3doaWxlKC0tZz49MCl7dmFyIGE9KGU9PTgpP2hbZ10mMjU1OmludEF0KGgsZyk7aWYoYTwwKXtpZihoLmNoYXJBdChnKT09XCItXCIpe2Q9dHJ1ZX1jb250aW51ZX1kPWZhbHNlO2lmKGY9PTApe3RoaXNbdGhpcy50KytdPWF9ZWxzZXtpZihmK2U+dGhpcy5EQil7dGhpc1t0aGlzLnQtMV18PShhJigoMTw8KHRoaXMuREItZikpLTEpKTw8Zjt0aGlzW3RoaXMudCsrXT0oYT4+KHRoaXMuREItZikpfWVsc2V7dGhpc1t0aGlzLnQtMV18PWE8PGZ9fWYrPWU7aWYoZj49dGhpcy5EQil7Zi09dGhpcy5EQn19aWYoZT09OCYmKGhbMF0mMTI4KSE9MCl7dGhpcy5zPS0xO2lmKGY+MCl7dGhpc1t0aGlzLnQtMV18PSgoMTw8KHRoaXMuREItZikpLTEpPDxmfX10aGlzLmNsYW1wKCk7aWYoZCl7QmlnSW50ZWdlci5aRVJPLnN1YlRvKHRoaXMsdGhpcyl9fWZ1bmN0aW9uIGJucENsYW1wKCl7dmFyIGE9dGhpcy5zJnRoaXMuRE07d2hpbGUodGhpcy50PjAmJnRoaXNbdGhpcy50LTFdPT1hKXstLXRoaXMudH19ZnVuY3Rpb24gYm5Ub1N0cmluZyhjKXtpZih0aGlzLnM8MCl7cmV0dXJuXCItXCIrdGhpcy5uZWdhdGUoKS50b1N0cmluZyhjKX12YXIgZTtpZihjPT0xNil7ZT00fWVsc2V7aWYoYz09OCl7ZT0zfWVsc2V7aWYoYz09Mil7ZT0xfWVsc2V7aWYoYz09MzIpe2U9NX1lbHNle2lmKGM9PTQpe2U9Mn1lbHNle3JldHVybiB0aGlzLnRvUmFkaXgoYyl9fX19fXZhciBnPSgxPDxlKS0xLGwsYT1mYWxzZSxoPVwiXCIsZj10aGlzLnQ7dmFyIGo9dGhpcy5EQi0oZip0aGlzLkRCKSVlO2lmKGYtLT4wKXtpZihqPHRoaXMuREImJihsPXRoaXNbZl0+PmopPjApe2E9dHJ1ZTtoPWludDJjaGFyKGwpfXdoaWxlKGY+PTApe2lmKGo8ZSl7bD0odGhpc1tmXSYoKDE8PGopLTEpKTw8KGUtaik7bHw9dGhpc1stLWZdPj4oais9dGhpcy5EQi1lKX1lbHNle2w9KHRoaXNbZl0+PihqLT1lKSkmZztpZihqPD0wKXtqKz10aGlzLkRCOy0tZn19aWYobD4wKXthPXRydWV9aWYoYSl7aCs9aW50MmNoYXIobCl9fX1yZXR1cm4gYT9oOlwiMFwifWZ1bmN0aW9uIGJuTmVnYXRlKCl7dmFyIGE9bmJpKCk7QmlnSW50ZWdlci5aRVJPLnN1YlRvKHRoaXMsYSk7cmV0dXJuIGF9ZnVuY3Rpb24gYm5BYnMoKXtyZXR1cm4odGhpcy5zPDApP3RoaXMubmVnYXRlKCk6dGhpc31mdW5jdGlvbiBibkNvbXBhcmVUbyhiKXt2YXIgZD10aGlzLnMtYi5zO2lmKGQhPTApe3JldHVybiBkfXZhciBjPXRoaXMudDtkPWMtYi50O2lmKGQhPTApe3JldHVybih0aGlzLnM8MCk/LWQ6ZH13aGlsZSgtLWM+PTApe2lmKChkPXRoaXNbY10tYltjXSkhPTApe3JldHVybiBkfX1yZXR1cm4gMH1mdW5jdGlvbiBuYml0cyhhKXt2YXIgYz0xLGI7aWYoKGI9YT4+PjE2KSE9MCl7YT1iO2MrPTE2fWlmKChiPWE+PjgpIT0wKXthPWI7Yys9OH1pZigoYj1hPj40KSE9MCl7YT1iO2MrPTR9aWYoKGI9YT4+MikhPTApe2E9YjtjKz0yfWlmKChiPWE+PjEpIT0wKXthPWI7Yys9MX1yZXR1cm4gY31mdW5jdGlvbiBibkJpdExlbmd0aCgpe2lmKHRoaXMudDw9MCl7cmV0dXJuIDB9cmV0dXJuIHRoaXMuREIqKHRoaXMudC0xKStuYml0cyh0aGlzW3RoaXMudC0xXV4odGhpcy5zJnRoaXMuRE0pKX1mdW5jdGlvbiBibnBETFNoaWZ0VG8oYyxiKXt2YXIgYTtmb3IoYT10aGlzLnQtMTthPj0wOy0tYSl7YlthK2NdPXRoaXNbYV19Zm9yKGE9Yy0xO2E+PTA7LS1hKXtiW2FdPTB9Yi50PXRoaXMudCtjO2Iucz10aGlzLnN9ZnVuY3Rpb24gYm5wRFJTaGlmdFRvKGMsYil7Zm9yKHZhciBhPWM7YTx0aGlzLnQ7KythKXtiW2EtY109dGhpc1thXX1iLnQ9TWF0aC5tYXgodGhpcy50LWMsMCk7Yi5zPXRoaXMuc31mdW5jdGlvbiBibnBMU2hpZnRUbyhqLGUpe3ZhciBiPWoldGhpcy5EQjt2YXIgYT10aGlzLkRCLWI7dmFyIGc9KDE8PGEpLTE7dmFyIGY9TWF0aC5mbG9vcihqL3RoaXMuREIpLGg9KHRoaXMuczw8YikmdGhpcy5ETSxkO2ZvcihkPXRoaXMudC0xO2Q+PTA7LS1kKXtlW2QrZisxXT0odGhpc1tkXT4+YSl8aDtoPSh0aGlzW2RdJmcpPDxifWZvcihkPWYtMTtkPj0wOy0tZCl7ZVtkXT0wfWVbZl09aDtlLnQ9dGhpcy50K2YrMTtlLnM9dGhpcy5zO2UuY2xhbXAoKX1mdW5jdGlvbiBibnBSU2hpZnRUbyhnLGQpe2Qucz10aGlzLnM7dmFyIGU9TWF0aC5mbG9vcihnL3RoaXMuREIpO2lmKGU+PXRoaXMudCl7ZC50PTA7cmV0dXJufXZhciBiPWcldGhpcy5EQjt2YXIgYT10aGlzLkRCLWI7dmFyIGY9KDE8PGIpLTE7ZFswXT10aGlzW2VdPj5iO2Zvcih2YXIgYz1lKzE7Yzx0aGlzLnQ7KytjKXtkW2MtZS0xXXw9KHRoaXNbY10mZik8PGE7ZFtjLWVdPXRoaXNbY10+PmJ9aWYoYj4wKXtkW3RoaXMudC1lLTFdfD0odGhpcy5zJmYpPDxhfWQudD10aGlzLnQtZTtkLmNsYW1wKCl9ZnVuY3Rpb24gYm5wU3ViVG8oZCxmKXt2YXIgZT0wLGc9MCxiPU1hdGgubWluKGQudCx0aGlzLnQpO3doaWxlKGU8Yil7Zys9dGhpc1tlXS1kW2VdO2ZbZSsrXT1nJnRoaXMuRE07Zz4+PXRoaXMuREJ9aWYoZC50PHRoaXMudCl7Zy09ZC5zO3doaWxlKGU8dGhpcy50KXtnKz10aGlzW2VdO2ZbZSsrXT1nJnRoaXMuRE07Zz4+PXRoaXMuREJ9Zys9dGhpcy5zfWVsc2V7Zys9dGhpcy5zO3doaWxlKGU8ZC50KXtnLT1kW2VdO2ZbZSsrXT1nJnRoaXMuRE07Zz4+PXRoaXMuREJ9Zy09ZC5zfWYucz0oZzwwKT8tMTowO2lmKGc8LTEpe2ZbZSsrXT10aGlzLkRWK2d9ZWxzZXtpZihnPjApe2ZbZSsrXT1nfX1mLnQ9ZTtmLmNsYW1wKCl9ZnVuY3Rpb24gYm5wTXVsdGlwbHlUbyhjLGUpe3ZhciBiPXRoaXMuYWJzKCksZj1jLmFicygpO3ZhciBkPWIudDtlLnQ9ZCtmLnQ7d2hpbGUoLS1kPj0wKXtlW2RdPTB9Zm9yKGQ9MDtkPGYudDsrK2Qpe2VbZCtiLnRdPWIuYW0oMCxmW2RdLGUsZCwwLGIudCl9ZS5zPTA7ZS5jbGFtcCgpO2lmKHRoaXMucyE9Yy5zKXtCaWdJbnRlZ2VyLlpFUk8uc3ViVG8oZSxlKX19ZnVuY3Rpb24gYm5wU3F1YXJlVG8oZCl7dmFyIGE9dGhpcy5hYnMoKTt2YXIgYj1kLnQ9MiphLnQ7d2hpbGUoLS1iPj0wKXtkW2JdPTB9Zm9yKGI9MDtiPGEudC0xOysrYil7dmFyIGU9YS5hbShiLGFbYl0sZCwyKmIsMCwxKTtpZigoZFtiK2EudF0rPWEuYW0oYisxLDIqYVtiXSxkLDIqYisxLGUsYS50LWItMSkpPj1hLkRWKXtkW2IrYS50XS09YS5EVjtkW2IrYS50KzFdPTF9fWlmKGQudD4wKXtkW2QudC0xXSs9YS5hbShiLGFbYl0sZCwyKmIsMCwxKX1kLnM9MDtkLmNsYW1wKCl9ZnVuY3Rpb24gYm5wRGl2UmVtVG8obixoLGcpe3ZhciB3PW4uYWJzKCk7aWYody50PD0wKXtyZXR1cm59dmFyIGs9dGhpcy5hYnMoKTtpZihrLnQ8dy50KXtpZihoIT1udWxsKXtoLmZyb21JbnQoMCl9aWYoZyE9bnVsbCl7dGhpcy5jb3B5VG8oZyl9cmV0dXJufWlmKGc9PW51bGwpe2c9bmJpKCl9dmFyIGQ9bmJpKCksYT10aGlzLnMsbD1uLnM7dmFyIHY9dGhpcy5EQi1uYml0cyh3W3cudC0xXSk7aWYodj4wKXt3LmxTaGlmdFRvKHYsZCk7ay5sU2hpZnRUbyh2LGcpfWVsc2V7dy5jb3B5VG8oZCk7ay5jb3B5VG8oZyl9dmFyIHA9ZC50O3ZhciBiPWRbcC0xXTtpZihiPT0wKXtyZXR1cm59dmFyIG89YiooMTw8dGhpcy5GMSkrKChwPjEpP2RbcC0yXT4+dGhpcy5GMjowKTt2YXIgQT10aGlzLkZWL28sej0oMTw8dGhpcy5GMSkvbyx4PTE8PHRoaXMuRjI7dmFyIHU9Zy50LHM9dS1wLGY9KGg9PW51bGwpP25iaSgpOmg7ZC5kbFNoaWZ0VG8ocyxmKTtpZihnLmNvbXBhcmVUbyhmKT49MCl7Z1tnLnQrK109MTtnLnN1YlRvKGYsZyl9QmlnSW50ZWdlci5PTkUuZGxTaGlmdFRvKHAsZik7Zi5zdWJUbyhkLGQpO3doaWxlKGQudDxwKXtkW2QudCsrXT0wfXdoaWxlKC0tcz49MCl7dmFyIGM9KGdbLS11XT09Yik/dGhpcy5ETTpNYXRoLmZsb29yKGdbdV0qQSsoZ1t1LTFdK3gpKnopO2lmKChnW3VdKz1kLmFtKDAsYyxnLHMsMCxwKSk8Yyl7ZC5kbFNoaWZ0VG8ocyxmKTtnLnN1YlRvKGYsZyk7d2hpbGUoZ1t1XTwtLWMpe2cuc3ViVG8oZixnKX19fWlmKGghPW51bGwpe2cuZHJTaGlmdFRvKHAsaCk7aWYoYSE9bCl7QmlnSW50ZWdlci5aRVJPLnN1YlRvKGgsaCl9fWcudD1wO2cuY2xhbXAoKTtpZih2PjApe2cuclNoaWZ0VG8odixnKX1pZihhPDApe0JpZ0ludGVnZXIuWkVSTy5zdWJUbyhnLGcpfX1mdW5jdGlvbiBibk1vZChiKXt2YXIgYz1uYmkoKTt0aGlzLmFicygpLmRpdlJlbVRvKGIsbnVsbCxjKTtpZih0aGlzLnM8MCYmYy5jb21wYXJlVG8oQmlnSW50ZWdlci5aRVJPKT4wKXtiLnN1YlRvKGMsYyl9cmV0dXJuIGN9ZnVuY3Rpb24gQ2xhc3NpYyhhKXt0aGlzLm09YX1mdW5jdGlvbiBjQ29udmVydChhKXtpZihhLnM8MHx8YS5jb21wYXJlVG8odGhpcy5tKT49MCl7cmV0dXJuIGEubW9kKHRoaXMubSl9ZWxzZXtyZXR1cm4gYX19ZnVuY3Rpb24gY1JldmVydChhKXtyZXR1cm4gYX1mdW5jdGlvbiBjUmVkdWNlKGEpe2EuZGl2UmVtVG8odGhpcy5tLG51bGwsYSl9ZnVuY3Rpb24gY011bFRvKGEsYyxiKXthLm11bHRpcGx5VG8oYyxiKTt0aGlzLnJlZHVjZShiKX1mdW5jdGlvbiBjU3FyVG8oYSxiKXthLnNxdWFyZVRvKGIpO3RoaXMucmVkdWNlKGIpfUNsYXNzaWMucHJvdG90eXBlLmNvbnZlcnQ9Y0NvbnZlcnQ7Q2xhc3NpYy5wcm90b3R5cGUucmV2ZXJ0PWNSZXZlcnQ7Q2xhc3NpYy5wcm90b3R5cGUucmVkdWNlPWNSZWR1Y2U7Q2xhc3NpYy5wcm90b3R5cGUubXVsVG89Y011bFRvO0NsYXNzaWMucHJvdG90eXBlLnNxclRvPWNTcXJUbztmdW5jdGlvbiBibnBJbnZEaWdpdCgpe2lmKHRoaXMudDwxKXtyZXR1cm4gMH12YXIgYT10aGlzWzBdO2lmKChhJjEpPT0wKXtyZXR1cm4gMH12YXIgYj1hJjM7Yj0oYiooMi0oYSYxNSkqYikpJjE1O2I9KGIqKDItKGEmMjU1KSpiKSkmMjU1O2I9KGIqKDItKCgoYSY2NTUzNSkqYikmNjU1MzUpKSkmNjU1MzU7Yj0oYiooMi1hKmIldGhpcy5EVikpJXRoaXMuRFY7cmV0dXJuKGI+MCk/dGhpcy5EVi1iOi1ifWZ1bmN0aW9uIE1vbnRnb21lcnkoYSl7dGhpcy5tPWE7dGhpcy5tcD1hLmludkRpZ2l0KCk7dGhpcy5tcGw9dGhpcy5tcCYzMjc2Nzt0aGlzLm1waD10aGlzLm1wPj4xNTt0aGlzLnVtPSgxPDwoYS5EQi0xNSkpLTE7dGhpcy5tdDI9MiphLnR9ZnVuY3Rpb24gbW9udENvbnZlcnQoYSl7dmFyIGI9bmJpKCk7YS5hYnMoKS5kbFNoaWZ0VG8odGhpcy5tLnQsYik7Yi5kaXZSZW1Ubyh0aGlzLm0sbnVsbCxiKTtpZihhLnM8MCYmYi5jb21wYXJlVG8oQmlnSW50ZWdlci5aRVJPKT4wKXt0aGlzLm0uc3ViVG8oYixiKX1yZXR1cm4gYn1mdW5jdGlvbiBtb250UmV2ZXJ0KGEpe3ZhciBiPW5iaSgpO2EuY29weVRvKGIpO3RoaXMucmVkdWNlKGIpO3JldHVybiBifWZ1bmN0aW9uIG1vbnRSZWR1Y2UoYSl7d2hpbGUoYS50PD10aGlzLm10Mil7YVthLnQrK109MH1mb3IodmFyIGM9MDtjPHRoaXMubS50OysrYyl7dmFyIGI9YVtjXSYzMjc2Nzt2YXIgZD0oYip0aGlzLm1wbCsoKChiKnRoaXMubXBoKyhhW2NdPj4xNSkqdGhpcy5tcGwpJnRoaXMudW0pPDwxNSkpJmEuRE07Yj1jK3RoaXMubS50O2FbYl0rPXRoaXMubS5hbSgwLGQsYSxjLDAsdGhpcy5tLnQpO3doaWxlKGFbYl0+PWEuRFYpe2FbYl0tPWEuRFY7YVsrK2JdKyt9fWEuY2xhbXAoKTthLmRyU2hpZnRUbyh0aGlzLm0udCxhKTtpZihhLmNvbXBhcmVUbyh0aGlzLm0pPj0wKXthLnN1YlRvKHRoaXMubSxhKX19ZnVuY3Rpb24gbW9udFNxclRvKGEsYil7YS5zcXVhcmVUbyhiKTt0aGlzLnJlZHVjZShiKX1mdW5jdGlvbiBtb250TXVsVG8oYSxjLGIpe2EubXVsdGlwbHlUbyhjLGIpO3RoaXMucmVkdWNlKGIpfU1vbnRnb21lcnkucHJvdG90eXBlLmNvbnZlcnQ9bW9udENvbnZlcnQ7TW9udGdvbWVyeS5wcm90b3R5cGUucmV2ZXJ0PW1vbnRSZXZlcnQ7TW9udGdvbWVyeS5wcm90b3R5cGUucmVkdWNlPW1vbnRSZWR1Y2U7TW9udGdvbWVyeS5wcm90b3R5cGUubXVsVG89bW9udE11bFRvO01vbnRnb21lcnkucHJvdG90eXBlLnNxclRvPW1vbnRTcXJUbztmdW5jdGlvbiBibnBJc0V2ZW4oKXtyZXR1cm4oKHRoaXMudD4wKT8odGhpc1swXSYxKTp0aGlzLnMpPT0wfWZ1bmN0aW9uIGJucEV4cChoLGope2lmKGg+NDI5NDk2NzI5NXx8aDwxKXtyZXR1cm4gQmlnSW50ZWdlci5PTkV9dmFyIGY9bmJpKCksYT1uYmkoKSxkPWouY29udmVydCh0aGlzKSxjPW5iaXRzKGgpLTE7ZC5jb3B5VG8oZik7d2hpbGUoLS1jPj0wKXtqLnNxclRvKGYsYSk7aWYoKGgmKDE8PGMpKT4wKXtqLm11bFRvKGEsZCxmKX1lbHNle3ZhciBiPWY7Zj1hO2E9Yn19cmV0dXJuIGoucmV2ZXJ0KGYpfWZ1bmN0aW9uIGJuTW9kUG93SW50KGIsYSl7dmFyIGM7aWYoYjwyNTZ8fGEuaXNFdmVuKCkpe2M9bmV3IENsYXNzaWMoYSl9ZWxzZXtjPW5ldyBNb250Z29tZXJ5KGEpfXJldHVybiB0aGlzLmV4cChiLGMpfUJpZ0ludGVnZXIucHJvdG90eXBlLmNvcHlUbz1ibnBDb3B5VG87QmlnSW50ZWdlci5wcm90b3R5cGUuZnJvbUludD1ibnBGcm9tSW50O0JpZ0ludGVnZXIucHJvdG90eXBlLmZyb21TdHJpbmc9Ym5wRnJvbVN0cmluZztCaWdJbnRlZ2VyLnByb3RvdHlwZS5jbGFtcD1ibnBDbGFtcDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5kbFNoaWZ0VG89Ym5wRExTaGlmdFRvO0JpZ0ludGVnZXIucHJvdG90eXBlLmRyU2hpZnRUbz1ibnBEUlNoaWZ0VG87QmlnSW50ZWdlci5wcm90b3R5cGUubFNoaWZ0VG89Ym5wTFNoaWZ0VG87QmlnSW50ZWdlci5wcm90b3R5cGUuclNoaWZ0VG89Ym5wUlNoaWZ0VG87QmlnSW50ZWdlci5wcm90b3R5cGUuc3ViVG89Ym5wU3ViVG87QmlnSW50ZWdlci5wcm90b3R5cGUubXVsdGlwbHlUbz1ibnBNdWx0aXBseVRvO0JpZ0ludGVnZXIucHJvdG90eXBlLnNxdWFyZVRvPWJucFNxdWFyZVRvO0JpZ0ludGVnZXIucHJvdG90eXBlLmRpdlJlbVRvPWJucERpdlJlbVRvO0JpZ0ludGVnZXIucHJvdG90eXBlLmludkRpZ2l0PWJucEludkRpZ2l0O0JpZ0ludGVnZXIucHJvdG90eXBlLmlzRXZlbj1ibnBJc0V2ZW47QmlnSW50ZWdlci5wcm90b3R5cGUuZXhwPWJucEV4cDtCaWdJbnRlZ2VyLnByb3RvdHlwZS50b1N0cmluZz1iblRvU3RyaW5nO0JpZ0ludGVnZXIucHJvdG90eXBlLm5lZ2F0ZT1ibk5lZ2F0ZTtCaWdJbnRlZ2VyLnByb3RvdHlwZS5hYnM9Ym5BYnM7QmlnSW50ZWdlci5wcm90b3R5cGUuY29tcGFyZVRvPWJuQ29tcGFyZVRvO0JpZ0ludGVnZXIucHJvdG90eXBlLmJpdExlbmd0aD1ibkJpdExlbmd0aDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5tb2Q9Ym5Nb2Q7QmlnSW50ZWdlci5wcm90b3R5cGUubW9kUG93SW50PWJuTW9kUG93SW50O0JpZ0ludGVnZXIuWkVSTz1uYnYoMCk7QmlnSW50ZWdlci5PTkU9bmJ2KDEpO1xuLyohIChjKSBUb20gV3UgfCBodHRwOi8vd3d3LWNzLXN0dWRlbnRzLnN0YW5mb3JkLmVkdS9+dGp3L2pzYm4vXHJcbiAqL1xyXG5mdW5jdGlvbiBibkNsb25lKCl7dmFyIGE9bmJpKCk7dGhpcy5jb3B5VG8oYSk7cmV0dXJuIGF9ZnVuY3Rpb24gYm5JbnRWYWx1ZSgpe2lmKHRoaXMuczwwKXtpZih0aGlzLnQ9PTEpe3JldHVybiB0aGlzWzBdLXRoaXMuRFZ9ZWxzZXtpZih0aGlzLnQ9PTApe3JldHVybiAtMX19fWVsc2V7aWYodGhpcy50PT0xKXtyZXR1cm4gdGhpc1swXX1lbHNle2lmKHRoaXMudD09MCl7cmV0dXJuIDB9fX1yZXR1cm4oKHRoaXNbMV0mKCgxPDwoMzItdGhpcy5EQikpLTEpKTw8dGhpcy5EQil8dGhpc1swXX1mdW5jdGlvbiBibkJ5dGVWYWx1ZSgpe3JldHVybih0aGlzLnQ9PTApP3RoaXMuczoodGhpc1swXTw8MjQpPj4yNH1mdW5jdGlvbiBiblNob3J0VmFsdWUoKXtyZXR1cm4odGhpcy50PT0wKT90aGlzLnM6KHRoaXNbMF08PDE2KT4+MTZ9ZnVuY3Rpb24gYm5wQ2h1bmtTaXplKGEpe3JldHVybiBNYXRoLmZsb29yKE1hdGguTE4yKnRoaXMuREIvTWF0aC5sb2coYSkpfWZ1bmN0aW9uIGJuU2lnTnVtKCl7aWYodGhpcy5zPDApe3JldHVybiAtMX1lbHNle2lmKHRoaXMudDw9MHx8KHRoaXMudD09MSYmdGhpc1swXTw9MCkpe3JldHVybiAwfWVsc2V7cmV0dXJuIDF9fX1mdW5jdGlvbiBibnBUb1JhZGl4KGMpe2lmKGM9PW51bGwpe2M9MTB9aWYodGhpcy5zaWdudW0oKT09MHx8YzwyfHxjPjM2KXtyZXR1cm5cIjBcIn12YXIgZj10aGlzLmNodW5rU2l6ZShjKTt2YXIgZT1NYXRoLnBvdyhjLGYpO3ZhciBpPW5idihlKSxqPW5iaSgpLGg9bmJpKCksZz1cIlwiO3RoaXMuZGl2UmVtVG8oaSxqLGgpO3doaWxlKGouc2lnbnVtKCk+MCl7Zz0oZStoLmludFZhbHVlKCkpLnRvU3RyaW5nKGMpLnN1YnN0cigxKStnO2ouZGl2UmVtVG8oaSxqLGgpfXJldHVybiBoLmludFZhbHVlKCkudG9TdHJpbmcoYykrZ31mdW5jdGlvbiBibnBGcm9tUmFkaXgobSxoKXt0aGlzLmZyb21JbnQoMCk7aWYoaD09bnVsbCl7aD0xMH12YXIgZj10aGlzLmNodW5rU2l6ZShoKTt2YXIgZz1NYXRoLnBvdyhoLGYpLGU9ZmFsc2UsYT0wLGw9MDtmb3IodmFyIGM9MDtjPG0ubGVuZ3RoOysrYyl7dmFyIGs9aW50QXQobSxjKTtpZihrPDApe2lmKG0uY2hhckF0KGMpPT1cIi1cIiYmdGhpcy5zaWdudW0oKT09MCl7ZT10cnVlfWNvbnRpbnVlfWw9aCpsK2s7aWYoKythPj1mKXt0aGlzLmRNdWx0aXBseShnKTt0aGlzLmRBZGRPZmZzZXQobCwwKTthPTA7bD0wfX1pZihhPjApe3RoaXMuZE11bHRpcGx5KE1hdGgucG93KGgsYSkpO3RoaXMuZEFkZE9mZnNldChsLDApfWlmKGUpe0JpZ0ludGVnZXIuWkVSTy5zdWJUbyh0aGlzLHRoaXMpfX1mdW5jdGlvbiBibnBGcm9tTnVtYmVyKGYsZSxoKXtpZihcIm51bWJlclwiPT10eXBlb2YgZSl7aWYoZjwyKXt0aGlzLmZyb21JbnQoMSl9ZWxzZXt0aGlzLmZyb21OdW1iZXIoZixoKTtpZighdGhpcy50ZXN0Qml0KGYtMSkpe3RoaXMuYml0d2lzZVRvKEJpZ0ludGVnZXIuT05FLnNoaWZ0TGVmdChmLTEpLG9wX29yLHRoaXMpfWlmKHRoaXMuaXNFdmVuKCkpe3RoaXMuZEFkZE9mZnNldCgxLDApfXdoaWxlKCF0aGlzLmlzUHJvYmFibGVQcmltZShlKSl7dGhpcy5kQWRkT2Zmc2V0KDIsMCk7aWYodGhpcy5iaXRMZW5ndGgoKT5mKXt0aGlzLnN1YlRvKEJpZ0ludGVnZXIuT05FLnNoaWZ0TGVmdChmLTEpLHRoaXMpfX19fWVsc2V7dmFyIGQ9bmV3IEFycmF5KCksZz1mJjc7ZC5sZW5ndGg9KGY+PjMpKzE7ZS5uZXh0Qnl0ZXMoZCk7aWYoZz4wKXtkWzBdJj0oKDE8PGcpLTEpfWVsc2V7ZFswXT0wfXRoaXMuZnJvbVN0cmluZyhkLDI1Nil9fWZ1bmN0aW9uIGJuVG9CeXRlQXJyYXkoKXt2YXIgYj10aGlzLnQsYz1uZXcgQXJyYXkoKTtjWzBdPXRoaXMuczt2YXIgZT10aGlzLkRCLShiKnRoaXMuREIpJTgsZixhPTA7aWYoYi0tPjApe2lmKGU8dGhpcy5EQiYmKGY9dGhpc1tiXT4+ZSkhPSh0aGlzLnMmdGhpcy5ETSk+PmUpe2NbYSsrXT1mfCh0aGlzLnM8PCh0aGlzLkRCLWUpKX13aGlsZShiPj0wKXtpZihlPDgpe2Y9KHRoaXNbYl0mKCgxPDxlKS0xKSk8PCg4LWUpO2Z8PXRoaXNbLS1iXT4+KGUrPXRoaXMuREItOCl9ZWxzZXtmPSh0aGlzW2JdPj4oZS09OCkpJjI1NTtpZihlPD0wKXtlKz10aGlzLkRCOy0tYn19aWYoKGYmMTI4KSE9MCl7Znw9LTI1Nn1pZihhPT0wJiYodGhpcy5zJjEyOCkhPShmJjEyOCkpeysrYX1pZihhPjB8fGYhPXRoaXMucyl7Y1thKytdPWZ9fX1yZXR1cm4gY31mdW5jdGlvbiBibkVxdWFscyhiKXtyZXR1cm4odGhpcy5jb21wYXJlVG8oYik9PTApfWZ1bmN0aW9uIGJuTWluKGIpe3JldHVybih0aGlzLmNvbXBhcmVUbyhiKTwwKT90aGlzOmJ9ZnVuY3Rpb24gYm5NYXgoYil7cmV0dXJuKHRoaXMuY29tcGFyZVRvKGIpPjApP3RoaXM6Yn1mdW5jdGlvbiBibnBCaXR3aXNlVG8oYyxoLGUpe3ZhciBkLGcsYj1NYXRoLm1pbihjLnQsdGhpcy50KTtmb3IoZD0wO2Q8YjsrK2Qpe2VbZF09aCh0aGlzW2RdLGNbZF0pfWlmKGMudDx0aGlzLnQpe2c9Yy5zJnRoaXMuRE07Zm9yKGQ9YjtkPHRoaXMudDsrK2Qpe2VbZF09aCh0aGlzW2RdLGcpfWUudD10aGlzLnR9ZWxzZXtnPXRoaXMucyZ0aGlzLkRNO2ZvcihkPWI7ZDxjLnQ7KytkKXtlW2RdPWgoZyxjW2RdKX1lLnQ9Yy50fWUucz1oKHRoaXMucyxjLnMpO2UuY2xhbXAoKX1mdW5jdGlvbiBvcF9hbmQoYSxiKXtyZXR1cm4gYSZifWZ1bmN0aW9uIGJuQW5kKGIpe3ZhciBjPW5iaSgpO3RoaXMuYml0d2lzZVRvKGIsb3BfYW5kLGMpO3JldHVybiBjfWZ1bmN0aW9uIG9wX29yKGEsYil7cmV0dXJuIGF8Yn1mdW5jdGlvbiBibk9yKGIpe3ZhciBjPW5iaSgpO3RoaXMuYml0d2lzZVRvKGIsb3Bfb3IsYyk7cmV0dXJuIGN9ZnVuY3Rpb24gb3BfeG9yKGEsYil7cmV0dXJuIGFeYn1mdW5jdGlvbiBiblhvcihiKXt2YXIgYz1uYmkoKTt0aGlzLmJpdHdpc2VUbyhiLG9wX3hvcixjKTtyZXR1cm4gY31mdW5jdGlvbiBvcF9hbmRub3QoYSxiKXtyZXR1cm4gYSZ+Yn1mdW5jdGlvbiBibkFuZE5vdChiKXt2YXIgYz1uYmkoKTt0aGlzLmJpdHdpc2VUbyhiLG9wX2FuZG5vdCxjKTtyZXR1cm4gY31mdW5jdGlvbiBibk5vdCgpe3ZhciBiPW5iaSgpO2Zvcih2YXIgYT0wO2E8dGhpcy50OysrYSl7YlthXT10aGlzLkRNJn50aGlzW2FdfWIudD10aGlzLnQ7Yi5zPX50aGlzLnM7cmV0dXJuIGJ9ZnVuY3Rpb24gYm5TaGlmdExlZnQoYil7dmFyIGE9bmJpKCk7aWYoYjwwKXt0aGlzLnJTaGlmdFRvKC1iLGEpfWVsc2V7dGhpcy5sU2hpZnRUbyhiLGEpfXJldHVybiBhfWZ1bmN0aW9uIGJuU2hpZnRSaWdodChiKXt2YXIgYT1uYmkoKTtpZihiPDApe3RoaXMubFNoaWZ0VG8oLWIsYSl9ZWxzZXt0aGlzLnJTaGlmdFRvKGIsYSl9cmV0dXJuIGF9ZnVuY3Rpb24gbGJpdChhKXtpZihhPT0wKXtyZXR1cm4gLTF9dmFyIGI9MDtpZigoYSY2NTUzNSk9PTApe2E+Pj0xNjtiKz0xNn1pZigoYSYyNTUpPT0wKXthPj49ODtiKz04fWlmKChhJjE1KT09MCl7YT4+PTQ7Yis9NH1pZigoYSYzKT09MCl7YT4+PTI7Yis9Mn1pZigoYSYxKT09MCl7KytifXJldHVybiBifWZ1bmN0aW9uIGJuR2V0TG93ZXN0U2V0Qml0KCl7Zm9yKHZhciBhPTA7YTx0aGlzLnQ7KythKXtpZih0aGlzW2FdIT0wKXtyZXR1cm4gYSp0aGlzLkRCK2xiaXQodGhpc1thXSl9fWlmKHRoaXMuczwwKXtyZXR1cm4gdGhpcy50KnRoaXMuREJ9cmV0dXJuIC0xfWZ1bmN0aW9uIGNiaXQoYSl7dmFyIGI9MDt3aGlsZShhIT0wKXthJj1hLTE7KytifXJldHVybiBifWZ1bmN0aW9uIGJuQml0Q291bnQoKXt2YXIgYz0wLGE9dGhpcy5zJnRoaXMuRE07Zm9yKHZhciBiPTA7Yjx0aGlzLnQ7KytiKXtjKz1jYml0KHRoaXNbYl1eYSl9cmV0dXJuIGN9ZnVuY3Rpb24gYm5UZXN0Qml0KGIpe3ZhciBhPU1hdGguZmxvb3IoYi90aGlzLkRCKTtpZihhPj10aGlzLnQpe3JldHVybih0aGlzLnMhPTApfXJldHVybigodGhpc1thXSYoMTw8KGIldGhpcy5EQikpKSE9MCl9ZnVuY3Rpb24gYm5wQ2hhbmdlQml0KGMsYil7dmFyIGE9QmlnSW50ZWdlci5PTkUuc2hpZnRMZWZ0KGMpO3RoaXMuYml0d2lzZVRvKGEsYixhKTtyZXR1cm4gYX1mdW5jdGlvbiBiblNldEJpdChhKXtyZXR1cm4gdGhpcy5jaGFuZ2VCaXQoYSxvcF9vcil9ZnVuY3Rpb24gYm5DbGVhckJpdChhKXtyZXR1cm4gdGhpcy5jaGFuZ2VCaXQoYSxvcF9hbmRub3QpfWZ1bmN0aW9uIGJuRmxpcEJpdChhKXtyZXR1cm4gdGhpcy5jaGFuZ2VCaXQoYSxvcF94b3IpfWZ1bmN0aW9uIGJucEFkZFRvKGQsZil7dmFyIGU9MCxnPTAsYj1NYXRoLm1pbihkLnQsdGhpcy50KTt3aGlsZShlPGIpe2crPXRoaXNbZV0rZFtlXTtmW2UrK109ZyZ0aGlzLkRNO2c+Pj10aGlzLkRCfWlmKGQudDx0aGlzLnQpe2crPWQuczt3aGlsZShlPHRoaXMudCl7Zys9dGhpc1tlXTtmW2UrK109ZyZ0aGlzLkRNO2c+Pj10aGlzLkRCfWcrPXRoaXMuc31lbHNle2crPXRoaXMuczt3aGlsZShlPGQudCl7Zys9ZFtlXTtmW2UrK109ZyZ0aGlzLkRNO2c+Pj10aGlzLkRCfWcrPWQuc31mLnM9KGc8MCk/LTE6MDtpZihnPjApe2ZbZSsrXT1nfWVsc2V7aWYoZzwtMSl7ZltlKytdPXRoaXMuRFYrZ319Zi50PWU7Zi5jbGFtcCgpfWZ1bmN0aW9uIGJuQWRkKGIpe3ZhciBjPW5iaSgpO3RoaXMuYWRkVG8oYixjKTtyZXR1cm4gY31mdW5jdGlvbiBiblN1YnRyYWN0KGIpe3ZhciBjPW5iaSgpO3RoaXMuc3ViVG8oYixjKTtyZXR1cm4gY31mdW5jdGlvbiBibk11bHRpcGx5KGIpe3ZhciBjPW5iaSgpO3RoaXMubXVsdGlwbHlUbyhiLGMpO3JldHVybiBjfWZ1bmN0aW9uIGJuU3F1YXJlKCl7dmFyIGE9bmJpKCk7dGhpcy5zcXVhcmVUbyhhKTtyZXR1cm4gYX1mdW5jdGlvbiBibkRpdmlkZShiKXt2YXIgYz1uYmkoKTt0aGlzLmRpdlJlbVRvKGIsYyxudWxsKTtyZXR1cm4gY31mdW5jdGlvbiBiblJlbWFpbmRlcihiKXt2YXIgYz1uYmkoKTt0aGlzLmRpdlJlbVRvKGIsbnVsbCxjKTtyZXR1cm4gY31mdW5jdGlvbiBibkRpdmlkZUFuZFJlbWFpbmRlcihiKXt2YXIgZD1uYmkoKSxjPW5iaSgpO3RoaXMuZGl2UmVtVG8oYixkLGMpO3JldHVybiBuZXcgQXJyYXkoZCxjKX1mdW5jdGlvbiBibnBETXVsdGlwbHkoYSl7dGhpc1t0aGlzLnRdPXRoaXMuYW0oMCxhLTEsdGhpcywwLDAsdGhpcy50KTsrK3RoaXMudDt0aGlzLmNsYW1wKCl9ZnVuY3Rpb24gYm5wREFkZE9mZnNldChiLGEpe2lmKGI9PTApe3JldHVybn13aGlsZSh0aGlzLnQ8PWEpe3RoaXNbdGhpcy50KytdPTB9dGhpc1thXSs9Yjt3aGlsZSh0aGlzW2FdPj10aGlzLkRWKXt0aGlzW2FdLT10aGlzLkRWO2lmKCsrYT49dGhpcy50KXt0aGlzW3RoaXMudCsrXT0wfSsrdGhpc1thXX19ZnVuY3Rpb24gTnVsbEV4cCgpe31mdW5jdGlvbiBuTm9wKGEpe3JldHVybiBhfWZ1bmN0aW9uIG5NdWxUbyhhLGMsYil7YS5tdWx0aXBseVRvKGMsYil9ZnVuY3Rpb24gblNxclRvKGEsYil7YS5zcXVhcmVUbyhiKX1OdWxsRXhwLnByb3RvdHlwZS5jb252ZXJ0PW5Ob3A7TnVsbEV4cC5wcm90b3R5cGUucmV2ZXJ0PW5Ob3A7TnVsbEV4cC5wcm90b3R5cGUubXVsVG89bk11bFRvO051bGxFeHAucHJvdG90eXBlLnNxclRvPW5TcXJUbztmdW5jdGlvbiBiblBvdyhhKXtyZXR1cm4gdGhpcy5leHAoYSxuZXcgTnVsbEV4cCgpKX1mdW5jdGlvbiBibnBNdWx0aXBseUxvd2VyVG8oYixmLGUpe3ZhciBkPU1hdGgubWluKHRoaXMudCtiLnQsZik7ZS5zPTA7ZS50PWQ7d2hpbGUoZD4wKXtlWy0tZF09MH12YXIgYztmb3IoYz1lLnQtdGhpcy50O2Q8YzsrK2Qpe2VbZCt0aGlzLnRdPXRoaXMuYW0oMCxiW2RdLGUsZCwwLHRoaXMudCl9Zm9yKGM9TWF0aC5taW4oYi50LGYpO2Q8YzsrK2Qpe3RoaXMuYW0oMCxiW2RdLGUsZCwwLGYtZCl9ZS5jbGFtcCgpfWZ1bmN0aW9uIGJucE11bHRpcGx5VXBwZXJUbyhiLGUsZCl7LS1lO3ZhciBjPWQudD10aGlzLnQrYi50LWU7ZC5zPTA7d2hpbGUoLS1jPj0wKXtkW2NdPTB9Zm9yKGM9TWF0aC5tYXgoZS10aGlzLnQsMCk7YzxiLnQ7KytjKXtkW3RoaXMudCtjLWVdPXRoaXMuYW0oZS1jLGJbY10sZCwwLDAsdGhpcy50K2MtZSl9ZC5jbGFtcCgpO2QuZHJTaGlmdFRvKDEsZCl9ZnVuY3Rpb24gQmFycmV0dChhKXt0aGlzLnIyPW5iaSgpO3RoaXMucTM9bmJpKCk7QmlnSW50ZWdlci5PTkUuZGxTaGlmdFRvKDIqYS50LHRoaXMucjIpO3RoaXMubXU9dGhpcy5yMi5kaXZpZGUoYSk7dGhpcy5tPWF9ZnVuY3Rpb24gYmFycmV0dENvbnZlcnQoYSl7aWYoYS5zPDB8fGEudD4yKnRoaXMubS50KXtyZXR1cm4gYS5tb2QodGhpcy5tKX1lbHNle2lmKGEuY29tcGFyZVRvKHRoaXMubSk8MCl7cmV0dXJuIGF9ZWxzZXt2YXIgYj1uYmkoKTthLmNvcHlUbyhiKTt0aGlzLnJlZHVjZShiKTtyZXR1cm4gYn19fWZ1bmN0aW9uIGJhcnJldHRSZXZlcnQoYSl7cmV0dXJuIGF9ZnVuY3Rpb24gYmFycmV0dFJlZHVjZShhKXthLmRyU2hpZnRUbyh0aGlzLm0udC0xLHRoaXMucjIpO2lmKGEudD50aGlzLm0udCsxKXthLnQ9dGhpcy5tLnQrMTthLmNsYW1wKCl9dGhpcy5tdS5tdWx0aXBseVVwcGVyVG8odGhpcy5yMix0aGlzLm0udCsxLHRoaXMucTMpO3RoaXMubS5tdWx0aXBseUxvd2VyVG8odGhpcy5xMyx0aGlzLm0udCsxLHRoaXMucjIpO3doaWxlKGEuY29tcGFyZVRvKHRoaXMucjIpPDApe2EuZEFkZE9mZnNldCgxLHRoaXMubS50KzEpfWEuc3ViVG8odGhpcy5yMixhKTt3aGlsZShhLmNvbXBhcmVUbyh0aGlzLm0pPj0wKXthLnN1YlRvKHRoaXMubSxhKX19ZnVuY3Rpb24gYmFycmV0dFNxclRvKGEsYil7YS5zcXVhcmVUbyhiKTt0aGlzLnJlZHVjZShiKX1mdW5jdGlvbiBiYXJyZXR0TXVsVG8oYSxjLGIpe2EubXVsdGlwbHlUbyhjLGIpO3RoaXMucmVkdWNlKGIpfUJhcnJldHQucHJvdG90eXBlLmNvbnZlcnQ9YmFycmV0dENvbnZlcnQ7QmFycmV0dC5wcm90b3R5cGUucmV2ZXJ0PWJhcnJldHRSZXZlcnQ7QmFycmV0dC5wcm90b3R5cGUucmVkdWNlPWJhcnJldHRSZWR1Y2U7QmFycmV0dC5wcm90b3R5cGUubXVsVG89YmFycmV0dE11bFRvO0JhcnJldHQucHJvdG90eXBlLnNxclRvPWJhcnJldHRTcXJUbztmdW5jdGlvbiBibk1vZFBvdyhxLGYpe3ZhciBvPXEuYml0TGVuZ3RoKCksaCxiPW5idigxKSx2O2lmKG88PTApe3JldHVybiBifWVsc2V7aWYobzwxOCl7aD0xfWVsc2V7aWYobzw0OCl7aD0zfWVsc2V7aWYobzwxNDQpe2g9NH1lbHNle2lmKG88NzY4KXtoPTV9ZWxzZXtoPTZ9fX19fWlmKG88OCl7dj1uZXcgQ2xhc3NpYyhmKX1lbHNle2lmKGYuaXNFdmVuKCkpe3Y9bmV3IEJhcnJldHQoZil9ZWxzZXt2PW5ldyBNb250Z29tZXJ5KGYpfX12YXIgcD1uZXcgQXJyYXkoKSxkPTMscz1oLTEsYT0oMTw8aCktMTtwWzFdPXYuY29udmVydCh0aGlzKTtpZihoPjEpe3ZhciBBPW5iaSgpO3Yuc3FyVG8ocFsxXSxBKTt3aGlsZShkPD1hKXtwW2RdPW5iaSgpO3YubXVsVG8oQSxwW2QtMl0scFtkXSk7ZCs9Mn19dmFyIGw9cS50LTEseCx1PXRydWUsYz1uYmkoKSx5O289bmJpdHMocVtsXSktMTt3aGlsZShsPj0wKXtpZihvPj1zKXt4PShxW2xdPj4oby1zKSkmYX1lbHNle3g9KHFbbF0mKCgxPDwobysxKSktMSkpPDwocy1vKTtpZihsPjApe3h8PXFbbC0xXT4+KHRoaXMuREIrby1zKX19ZD1oO3doaWxlKCh4JjEpPT0wKXt4Pj49MTstLWR9aWYoKG8tPWQpPDApe28rPXRoaXMuREI7LS1sfWlmKHUpe3BbeF0uY29weVRvKGIpO3U9ZmFsc2V9ZWxzZXt3aGlsZShkPjEpe3Yuc3FyVG8oYixjKTt2LnNxclRvKGMsYik7ZC09Mn1pZihkPjApe3Yuc3FyVG8oYixjKX1lbHNle3k9YjtiPWM7Yz15fXYubXVsVG8oYyxwW3hdLGIpfXdoaWxlKGw+PTAmJihxW2xdJigxPDxvKSk9PTApe3Yuc3FyVG8oYixjKTt5PWI7Yj1jO2M9eTtpZigtLW88MCl7bz10aGlzLkRCLTE7LS1sfX19cmV0dXJuIHYucmV2ZXJ0KGIpfWZ1bmN0aW9uIGJuR0NEKGMpe3ZhciBiPSh0aGlzLnM8MCk/dGhpcy5uZWdhdGUoKTp0aGlzLmNsb25lKCk7dmFyIGg9KGMuczwwKT9jLm5lZ2F0ZSgpOmMuY2xvbmUoKTtpZihiLmNvbXBhcmVUbyhoKTwwKXt2YXIgZT1iO2I9aDtoPWV9dmFyIGQ9Yi5nZXRMb3dlc3RTZXRCaXQoKSxmPWguZ2V0TG93ZXN0U2V0Qml0KCk7aWYoZjwwKXtyZXR1cm4gYn1pZihkPGYpe2Y9ZH1pZihmPjApe2IuclNoaWZ0VG8oZixiKTtoLnJTaGlmdFRvKGYsaCl9d2hpbGUoYi5zaWdudW0oKT4wKXtpZigoZD1iLmdldExvd2VzdFNldEJpdCgpKT4wKXtiLnJTaGlmdFRvKGQsYil9aWYoKGQ9aC5nZXRMb3dlc3RTZXRCaXQoKSk+MCl7aC5yU2hpZnRUbyhkLGgpfWlmKGIuY29tcGFyZVRvKGgpPj0wKXtiLnN1YlRvKGgsYik7Yi5yU2hpZnRUbygxLGIpfWVsc2V7aC5zdWJUbyhiLGgpO2guclNoaWZ0VG8oMSxoKX19aWYoZj4wKXtoLmxTaGlmdFRvKGYsaCl9cmV0dXJuIGh9ZnVuY3Rpb24gYm5wTW9kSW50KGUpe2lmKGU8PTApe3JldHVybiAwfXZhciBjPXRoaXMuRFYlZSxiPSh0aGlzLnM8MCk/ZS0xOjA7aWYodGhpcy50PjApe2lmKGM9PTApe2I9dGhpc1swXSVlfWVsc2V7Zm9yKHZhciBhPXRoaXMudC0xO2E+PTA7LS1hKXtiPShjKmIrdGhpc1thXSklZX19fXJldHVybiBifWZ1bmN0aW9uIGJuTW9kSW52ZXJzZShmKXt2YXIgaj1mLmlzRXZlbigpO2lmKCh0aGlzLmlzRXZlbigpJiZqKXx8Zi5zaWdudW0oKT09MCl7cmV0dXJuIEJpZ0ludGVnZXIuWkVST312YXIgaT1mLmNsb25lKCksaD10aGlzLmNsb25lKCk7dmFyIGc9bmJ2KDEpLGU9bmJ2KDApLGw9bmJ2KDApLGs9bmJ2KDEpO3doaWxlKGkuc2lnbnVtKCkhPTApe3doaWxlKGkuaXNFdmVuKCkpe2kuclNoaWZ0VG8oMSxpKTtpZihqKXtpZighZy5pc0V2ZW4oKXx8IWUuaXNFdmVuKCkpe2cuYWRkVG8odGhpcyxnKTtlLnN1YlRvKGYsZSl9Zy5yU2hpZnRUbygxLGcpfWVsc2V7aWYoIWUuaXNFdmVuKCkpe2Uuc3ViVG8oZixlKX19ZS5yU2hpZnRUbygxLGUpfXdoaWxlKGguaXNFdmVuKCkpe2guclNoaWZ0VG8oMSxoKTtpZihqKXtpZighbC5pc0V2ZW4oKXx8IWsuaXNFdmVuKCkpe2wuYWRkVG8odGhpcyxsKTtrLnN1YlRvKGYsayl9bC5yU2hpZnRUbygxLGwpfWVsc2V7aWYoIWsuaXNFdmVuKCkpe2suc3ViVG8oZixrKX19ay5yU2hpZnRUbygxLGspfWlmKGkuY29tcGFyZVRvKGgpPj0wKXtpLnN1YlRvKGgsaSk7aWYoail7Zy5zdWJUbyhsLGcpfWUuc3ViVG8oayxlKX1lbHNle2guc3ViVG8oaSxoKTtpZihqKXtsLnN1YlRvKGcsbCl9ay5zdWJUbyhlLGspfX1pZihoLmNvbXBhcmVUbyhCaWdJbnRlZ2VyLk9ORSkhPTApe3JldHVybiBCaWdJbnRlZ2VyLlpFUk99aWYoay5jb21wYXJlVG8oZik+PTApe3JldHVybiBrLnN1YnRyYWN0KGYpfWlmKGsuc2lnbnVtKCk8MCl7ay5hZGRUbyhmLGspfWVsc2V7cmV0dXJuIGt9aWYoay5zaWdudW0oKTwwKXtyZXR1cm4gay5hZGQoZil9ZWxzZXtyZXR1cm4ga319dmFyIGxvd3ByaW1lcz1bMiwzLDUsNywxMSwxMywxNywxOSwyMywyOSwzMSwzNyw0MSw0Myw0Nyw1Myw1OSw2MSw2Nyw3MSw3Myw3OSw4Myw4OSw5NywxMDEsMTAzLDEwNywxMDksMTEzLDEyNywxMzEsMTM3LDEzOSwxNDksMTUxLDE1NywxNjMsMTY3LDE3MywxNzksMTgxLDE5MSwxOTMsMTk3LDE5OSwyMTEsMjIzLDIyNywyMjksMjMzLDIzOSwyNDEsMjUxLDI1NywyNjMsMjY5LDI3MSwyNzcsMjgxLDI4MywyOTMsMzA3LDMxMSwzMTMsMzE3LDMzMSwzMzcsMzQ3LDM0OSwzNTMsMzU5LDM2NywzNzMsMzc5LDM4MywzODksMzk3LDQwMSw0MDksNDE5LDQyMSw0MzEsNDMzLDQzOSw0NDMsNDQ5LDQ1Nyw0NjEsNDYzLDQ2Nyw0NzksNDg3LDQ5MSw0OTksNTAzLDUwOSw1MjEsNTIzLDU0MSw1NDcsNTU3LDU2Myw1NjksNTcxLDU3Nyw1ODcsNTkzLDU5OSw2MDEsNjA3LDYxMyw2MTcsNjE5LDYzMSw2NDEsNjQzLDY0Nyw2NTMsNjU5LDY2MSw2NzMsNjc3LDY4Myw2OTEsNzAxLDcwOSw3MTksNzI3LDczMyw3MzksNzQzLDc1MSw3NTcsNzYxLDc2OSw3NzMsNzg3LDc5Nyw4MDksODExLDgyMSw4MjMsODI3LDgyOSw4MzksODUzLDg1Nyw4NTksODYzLDg3Nyw4ODEsODgzLDg4Nyw5MDcsOTExLDkxOSw5MjksOTM3LDk0MSw5NDcsOTUzLDk2Nyw5NzEsOTc3LDk4Myw5OTEsOTk3XTt2YXIgbHBsaW09KDE8PDI2KS9sb3dwcmltZXNbbG93cHJpbWVzLmxlbmd0aC0xXTtmdW5jdGlvbiBibklzUHJvYmFibGVQcmltZShlKXt2YXIgZCxiPXRoaXMuYWJzKCk7aWYoYi50PT0xJiZiWzBdPD1sb3dwcmltZXNbbG93cHJpbWVzLmxlbmd0aC0xXSl7Zm9yKGQ9MDtkPGxvd3ByaW1lcy5sZW5ndGg7KytkKXtpZihiWzBdPT1sb3dwcmltZXNbZF0pe3JldHVybiB0cnVlfX1yZXR1cm4gZmFsc2V9aWYoYi5pc0V2ZW4oKSl7cmV0dXJuIGZhbHNlfWQ9MTt3aGlsZShkPGxvd3ByaW1lcy5sZW5ndGgpe3ZhciBhPWxvd3ByaW1lc1tkXSxjPWQrMTt3aGlsZShjPGxvd3ByaW1lcy5sZW5ndGgmJmE8bHBsaW0pe2EqPWxvd3ByaW1lc1tjKytdfWE9Yi5tb2RJbnQoYSk7d2hpbGUoZDxjKXtpZihhJWxvd3ByaW1lc1tkKytdPT0wKXtyZXR1cm4gZmFsc2V9fX1yZXR1cm4gYi5taWxsZXJSYWJpbihlKX1mdW5jdGlvbiBibnBNaWxsZXJSYWJpbihmKXt2YXIgZz10aGlzLnN1YnRyYWN0KEJpZ0ludGVnZXIuT05FKTt2YXIgYz1nLmdldExvd2VzdFNldEJpdCgpO2lmKGM8PTApe3JldHVybiBmYWxzZX12YXIgaD1nLnNoaWZ0UmlnaHQoYyk7Zj0oZisxKT4+MTtpZihmPmxvd3ByaW1lcy5sZW5ndGgpe2Y9bG93cHJpbWVzLmxlbmd0aH12YXIgYj1uYmkoKTtmb3IodmFyIGU9MDtlPGY7KytlKXtiLmZyb21JbnQobG93cHJpbWVzW01hdGguZmxvb3IoTWF0aC5yYW5kb20oKSpsb3dwcmltZXMubGVuZ3RoKV0pO3ZhciBsPWIubW9kUG93KGgsdGhpcyk7aWYobC5jb21wYXJlVG8oQmlnSW50ZWdlci5PTkUpIT0wJiZsLmNvbXBhcmVUbyhnKSE9MCl7dmFyIGQ9MTt3aGlsZShkKys8YyYmbC5jb21wYXJlVG8oZykhPTApe2w9bC5tb2RQb3dJbnQoMix0aGlzKTtpZihsLmNvbXBhcmVUbyhCaWdJbnRlZ2VyLk9ORSk9PTApe3JldHVybiBmYWxzZX19aWYobC5jb21wYXJlVG8oZykhPTApe3JldHVybiBmYWxzZX19fXJldHVybiB0cnVlfUJpZ0ludGVnZXIucHJvdG90eXBlLmNodW5rU2l6ZT1ibnBDaHVua1NpemU7QmlnSW50ZWdlci5wcm90b3R5cGUudG9SYWRpeD1ibnBUb1JhZGl4O0JpZ0ludGVnZXIucHJvdG90eXBlLmZyb21SYWRpeD1ibnBGcm9tUmFkaXg7QmlnSW50ZWdlci5wcm90b3R5cGUuZnJvbU51bWJlcj1ibnBGcm9tTnVtYmVyO0JpZ0ludGVnZXIucHJvdG90eXBlLmJpdHdpc2VUbz1ibnBCaXR3aXNlVG87QmlnSW50ZWdlci5wcm90b3R5cGUuY2hhbmdlQml0PWJucENoYW5nZUJpdDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5hZGRUbz1ibnBBZGRUbztCaWdJbnRlZ2VyLnByb3RvdHlwZS5kTXVsdGlwbHk9Ym5wRE11bHRpcGx5O0JpZ0ludGVnZXIucHJvdG90eXBlLmRBZGRPZmZzZXQ9Ym5wREFkZE9mZnNldDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5tdWx0aXBseUxvd2VyVG89Ym5wTXVsdGlwbHlMb3dlclRvO0JpZ0ludGVnZXIucHJvdG90eXBlLm11bHRpcGx5VXBwZXJUbz1ibnBNdWx0aXBseVVwcGVyVG87QmlnSW50ZWdlci5wcm90b3R5cGUubW9kSW50PWJucE1vZEludDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5taWxsZXJSYWJpbj1ibnBNaWxsZXJSYWJpbjtCaWdJbnRlZ2VyLnByb3RvdHlwZS5jbG9uZT1ibkNsb25lO0JpZ0ludGVnZXIucHJvdG90eXBlLmludFZhbHVlPWJuSW50VmFsdWU7QmlnSW50ZWdlci5wcm90b3R5cGUuYnl0ZVZhbHVlPWJuQnl0ZVZhbHVlO0JpZ0ludGVnZXIucHJvdG90eXBlLnNob3J0VmFsdWU9Ym5TaG9ydFZhbHVlO0JpZ0ludGVnZXIucHJvdG90eXBlLnNpZ251bT1iblNpZ051bTtCaWdJbnRlZ2VyLnByb3RvdHlwZS50b0J5dGVBcnJheT1iblRvQnl0ZUFycmF5O0JpZ0ludGVnZXIucHJvdG90eXBlLmVxdWFscz1ibkVxdWFscztCaWdJbnRlZ2VyLnByb3RvdHlwZS5taW49Ym5NaW47QmlnSW50ZWdlci5wcm90b3R5cGUubWF4PWJuTWF4O0JpZ0ludGVnZXIucHJvdG90eXBlLmFuZD1ibkFuZDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5vcj1ibk9yO0JpZ0ludGVnZXIucHJvdG90eXBlLnhvcj1iblhvcjtCaWdJbnRlZ2VyLnByb3RvdHlwZS5hbmROb3Q9Ym5BbmROb3Q7QmlnSW50ZWdlci5wcm90b3R5cGUubm90PWJuTm90O0JpZ0ludGVnZXIucHJvdG90eXBlLnNoaWZ0TGVmdD1iblNoaWZ0TGVmdDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5zaGlmdFJpZ2h0PWJuU2hpZnRSaWdodDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5nZXRMb3dlc3RTZXRCaXQ9Ym5HZXRMb3dlc3RTZXRCaXQ7QmlnSW50ZWdlci5wcm90b3R5cGUuYml0Q291bnQ9Ym5CaXRDb3VudDtCaWdJbnRlZ2VyLnByb3RvdHlwZS50ZXN0Qml0PWJuVGVzdEJpdDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5zZXRCaXQ9Ym5TZXRCaXQ7QmlnSW50ZWdlci5wcm90b3R5cGUuY2xlYXJCaXQ9Ym5DbGVhckJpdDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5mbGlwQml0PWJuRmxpcEJpdDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5hZGQ9Ym5BZGQ7QmlnSW50ZWdlci5wcm90b3R5cGUuc3VidHJhY3Q9Ym5TdWJ0cmFjdDtCaWdJbnRlZ2VyLnByb3RvdHlwZS5tdWx0aXBseT1ibk11bHRpcGx5O0JpZ0ludGVnZXIucHJvdG90eXBlLmRpdmlkZT1ibkRpdmlkZTtCaWdJbnRlZ2VyLnByb3RvdHlwZS5yZW1haW5kZXI9Ym5SZW1haW5kZXI7QmlnSW50ZWdlci5wcm90b3R5cGUuZGl2aWRlQW5kUmVtYWluZGVyPWJuRGl2aWRlQW5kUmVtYWluZGVyO0JpZ0ludGVnZXIucHJvdG90eXBlLm1vZFBvdz1ibk1vZFBvdztCaWdJbnRlZ2VyLnByb3RvdHlwZS5tb2RJbnZlcnNlPWJuTW9kSW52ZXJzZTtCaWdJbnRlZ2VyLnByb3RvdHlwZS5wb3c9Ym5Qb3c7QmlnSW50ZWdlci5wcm90b3R5cGUuZ2NkPWJuR0NEO0JpZ0ludGVnZXIucHJvdG90eXBlLmlzUHJvYmFibGVQcmltZT1ibklzUHJvYmFibGVQcmltZTtCaWdJbnRlZ2VyLnByb3RvdHlwZS5zcXVhcmU9Ym5TcXVhcmU7XG4vKiEgKGMpIFRvbSBXdSB8IGh0dHA6Ly93d3ctY3Mtc3R1ZGVudHMuc3RhbmZvcmQuZWR1L350ancvanNibi9cclxuICovXHJcbmZ1bmN0aW9uIEFyY2ZvdXIoKXt0aGlzLmk9MDt0aGlzLmo9MDt0aGlzLlM9bmV3IEFycmF5KCl9ZnVuY3Rpb24gQVJDNGluaXQoZCl7dmFyIGMsYSxiO2ZvcihjPTA7YzwyNTY7KytjKXt0aGlzLlNbY109Y31hPTA7Zm9yKGM9MDtjPDI1NjsrK2Mpe2E9KGErdGhpcy5TW2NdK2RbYyVkLmxlbmd0aF0pJjI1NTtiPXRoaXMuU1tjXTt0aGlzLlNbY109dGhpcy5TW2FdO3RoaXMuU1thXT1ifXRoaXMuaT0wO3RoaXMuaj0wfWZ1bmN0aW9uIEFSQzRuZXh0KCl7dmFyIGE7dGhpcy5pPSh0aGlzLmkrMSkmMjU1O3RoaXMuaj0odGhpcy5qK3RoaXMuU1t0aGlzLmldKSYyNTU7YT10aGlzLlNbdGhpcy5pXTt0aGlzLlNbdGhpcy5pXT10aGlzLlNbdGhpcy5qXTt0aGlzLlNbdGhpcy5qXT1hO3JldHVybiB0aGlzLlNbKGErdGhpcy5TW3RoaXMuaV0pJjI1NV19QXJjZm91ci5wcm90b3R5cGUuaW5pdD1BUkM0aW5pdDtBcmNmb3VyLnByb3RvdHlwZS5uZXh0PUFSQzRuZXh0O2Z1bmN0aW9uIHBybmdfbmV3c3RhdGUoKXtyZXR1cm4gbmV3IEFyY2ZvdXIoKX12YXIgcm5nX3BzaXplPTI1Njtcbi8qISAoYykgVG9tIFd1IHwgaHR0cDovL3d3dy1jcy1zdHVkZW50cy5zdGFuZm9yZC5lZHUvfnRqdy9qc2JuL1xyXG4gKi9cclxudmFyIHJuZ19zdGF0ZTt2YXIgcm5nX3Bvb2w7dmFyIHJuZ19wcHRyO2Z1bmN0aW9uIHJuZ19zZWVkX2ludChhKXtybmdfcG9vbFtybmdfcHB0cisrXV49YSYyNTU7cm5nX3Bvb2xbcm5nX3BwdHIrK11ePShhPj44KSYyNTU7cm5nX3Bvb2xbcm5nX3BwdHIrK11ePShhPj4xNikmMjU1O3JuZ19wb29sW3JuZ19wcHRyKytdXj0oYT4+MjQpJjI1NTtpZihybmdfcHB0cj49cm5nX3BzaXplKXtybmdfcHB0ci09cm5nX3BzaXplfX1mdW5jdGlvbiBybmdfc2VlZF90aW1lKCl7cm5nX3NlZWRfaW50KG5ldyBEYXRlKCkuZ2V0VGltZSgpKX1pZihybmdfcG9vbD09bnVsbCl7cm5nX3Bvb2w9bmV3IEFycmF5KCk7cm5nX3BwdHI9MDt2YXIgdDtpZih3aW5kb3chPT11bmRlZmluZWQmJih3aW5kb3cuY3J5cHRvIT09dW5kZWZpbmVkfHx3aW5kb3cubXNDcnlwdG8hPT11bmRlZmluZWQpKXt2YXIgY3J5cHRvPXdpbmRvdy5jcnlwdG98fHdpbmRvdy5tc0NyeXB0bztpZihjcnlwdG8uZ2V0UmFuZG9tVmFsdWVzKXt2YXIgdWE9bmV3IFVpbnQ4QXJyYXkoMzIpO2NyeXB0by5nZXRSYW5kb21WYWx1ZXModWEpO2Zvcih0PTA7dDwzMjsrK3Qpe3JuZ19wb29sW3JuZ19wcHRyKytdPXVhW3RdfX1lbHNle2lmKG5hdmlnYXRvci5hcHBOYW1lPT1cIk5ldHNjYXBlXCImJm5hdmlnYXRvci5hcHBWZXJzaW9uPFwiNVwiKXt2YXIgej13aW5kb3cuY3J5cHRvLnJhbmRvbSgzMik7Zm9yKHQ9MDt0PHoubGVuZ3RoOysrdCl7cm5nX3Bvb2xbcm5nX3BwdHIrK109ei5jaGFyQ29kZUF0KHQpJjI1NX19fX13aGlsZShybmdfcHB0cjxybmdfcHNpemUpe3Q9TWF0aC5mbG9vcig2NTUzNipNYXRoLnJhbmRvbSgpKTtybmdfcG9vbFtybmdfcHB0cisrXT10Pj4+ODtybmdfcG9vbFtybmdfcHB0cisrXT10JjI1NX1ybmdfcHB0cj0wO3JuZ19zZWVkX3RpbWUoKX1mdW5jdGlvbiBybmdfZ2V0X2J5dGUoKXtpZihybmdfc3RhdGU9PW51bGwpe3JuZ19zZWVkX3RpbWUoKTtybmdfc3RhdGU9cHJuZ19uZXdzdGF0ZSgpO3JuZ19zdGF0ZS5pbml0KHJuZ19wb29sKTtmb3Iocm5nX3BwdHI9MDtybmdfcHB0cjxybmdfcG9vbC5sZW5ndGg7KytybmdfcHB0cil7cm5nX3Bvb2xbcm5nX3BwdHJdPTB9cm5nX3BwdHI9MH1yZXR1cm4gcm5nX3N0YXRlLm5leHQoKX1mdW5jdGlvbiBybmdfZ2V0X2J5dGVzKGIpe3ZhciBhO2ZvcihhPTA7YTxiLmxlbmd0aDsrK2Epe2JbYV09cm5nX2dldF9ieXRlKCl9fWZ1bmN0aW9uIFNlY3VyZVJhbmRvbSgpe31TZWN1cmVSYW5kb20ucHJvdG90eXBlLm5leHRCeXRlcz1ybmdfZ2V0X2J5dGVzO1xuLyohIChjKSBUb20gV3UgfCBodHRwOi8vd3d3LWNzLXN0dWRlbnRzLnN0YW5mb3JkLmVkdS9+dGp3L2pzYm4vXHJcbiAqL1xyXG5mdW5jdGlvbiBwYXJzZUJpZ0ludChiLGEpe3JldHVybiBuZXcgQmlnSW50ZWdlcihiLGEpfWZ1bmN0aW9uIGxpbmVicmsoYyxkKXt2YXIgYT1cIlwiO3ZhciBiPTA7d2hpbGUoYitkPGMubGVuZ3RoKXthKz1jLnN1YnN0cmluZyhiLGIrZCkrXCJcXG5cIjtiKz1kfXJldHVybiBhK2Muc3Vic3RyaW5nKGIsYy5sZW5ndGgpfWZ1bmN0aW9uIGJ5dGUySGV4KGEpe2lmKGE8MTYpe3JldHVyblwiMFwiK2EudG9TdHJpbmcoMTYpfWVsc2V7cmV0dXJuIGEudG9TdHJpbmcoMTYpfX1mdW5jdGlvbiBwa2NzMXBhZDIoZSxoKXtpZihoPGUubGVuZ3RoKzExKXt0aHJvd1wiTWVzc2FnZSB0b28gbG9uZyBmb3IgUlNBXCI7cmV0dXJuIG51bGx9dmFyIGc9bmV3IEFycmF5KCk7dmFyIGQ9ZS5sZW5ndGgtMTt3aGlsZShkPj0wJiZoPjApe3ZhciBmPWUuY2hhckNvZGVBdChkLS0pO2lmKGY8MTI4KXtnWy0taF09Zn1lbHNle2lmKChmPjEyNykmJihmPDIwNDgpKXtnWy0taF09KGYmNjMpfDEyODtnWy0taF09KGY+PjYpfDE5Mn1lbHNle2dbLS1oXT0oZiY2Myl8MTI4O2dbLS1oXT0oKGY+PjYpJjYzKXwxMjg7Z1stLWhdPShmPj4xMil8MjI0fX19Z1stLWhdPTA7dmFyIGI9bmV3IFNlY3VyZVJhbmRvbSgpO3ZhciBhPW5ldyBBcnJheSgpO3doaWxlKGg+Mil7YVswXT0wO3doaWxlKGFbMF09PTApe2IubmV4dEJ5dGVzKGEpfWdbLS1oXT1hWzBdfWdbLS1oXT0yO2dbLS1oXT0wO3JldHVybiBuZXcgQmlnSW50ZWdlcihnKX1mdW5jdGlvbiBvYWVwX21nZjFfYXJyKGMsYSxlKXt2YXIgYj1cIlwiLGQ9MDt3aGlsZShiLmxlbmd0aDxhKXtiKz1lKFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkoU3RyaW5nLGMuY29uY2F0KFsoZCY0Mjc4MTkwMDgwKT4+MjQsKGQmMTY3MTE2ODApPj4xNiwoZCY2NTI4MCk+PjgsZCYyNTVdKSkpO2QrPTF9cmV0dXJuIGJ9ZnVuY3Rpb24gb2FlcF9wYWQocSxhLGYsbCl7dmFyIGM9S0pVUi5jcnlwdG8uTWVzc2FnZURpZ2VzdDt2YXIgbz1LSlVSLmNyeXB0by5VdGlsO3ZhciBiPW51bGw7aWYoIWYpe2Y9XCJzaGExXCJ9aWYodHlwZW9mIGY9PT1cInN0cmluZ1wiKXtiPWMuZ2V0Q2Fub25pY2FsQWxnTmFtZShmKTtsPWMuZ2V0SGFzaExlbmd0aChiKTtmPWZ1bmN0aW9uKGkpe3JldHVybiBoZXh0b3JzdHIoby5oYXNoSGV4KHJzdHJ0b2hleChpKSxiKSl9fWlmKHEubGVuZ3RoKzIqbCsyPmEpe3Rocm93XCJNZXNzYWdlIHRvbyBsb25nIGZvciBSU0FcIn12YXIgaz1cIlwiLGU7Zm9yKGU9MDtlPGEtcS5sZW5ndGgtMipsLTI7ZSs9MSl7ays9XCJcXHgwMFwifXZhciBoPWYoXCJcIikraytcIlxceDAxXCIrcTt2YXIgZz1uZXcgQXJyYXkobCk7bmV3IFNlY3VyZVJhbmRvbSgpLm5leHRCeXRlcyhnKTt2YXIgaj1vYWVwX21nZjFfYXJyKGcsaC5sZW5ndGgsZik7dmFyIHA9W107Zm9yKGU9MDtlPGgubGVuZ3RoO2UrPTEpe3BbZV09aC5jaGFyQ29kZUF0KGUpXmouY2hhckNvZGVBdChlKX12YXIgbT1vYWVwX21nZjFfYXJyKHAsZy5sZW5ndGgsZik7dmFyIGQ9WzBdO2ZvcihlPTA7ZTxnLmxlbmd0aDtlKz0xKXtkW2UrMV09Z1tlXV5tLmNoYXJDb2RlQXQoZSl9cmV0dXJuIG5ldyBCaWdJbnRlZ2VyKGQuY29uY2F0KHApKX1mdW5jdGlvbiBSU0FLZXkoKXt0aGlzLm49bnVsbDt0aGlzLmU9MDt0aGlzLmQ9bnVsbDt0aGlzLnA9bnVsbDt0aGlzLnE9bnVsbDt0aGlzLmRtcDE9bnVsbDt0aGlzLmRtcTE9bnVsbDt0aGlzLmNvZWZmPW51bGx9ZnVuY3Rpb24gUlNBU2V0UHVibGljKGIsYSl7dGhpcy5pc1B1YmxpYz10cnVlO3RoaXMuaXNQcml2YXRlPWZhbHNlO2lmKHR5cGVvZiBiIT09XCJzdHJpbmdcIil7dGhpcy5uPWI7dGhpcy5lPWF9ZWxzZXtpZihiIT1udWxsJiZhIT1udWxsJiZiLmxlbmd0aD4wJiZhLmxlbmd0aD4wKXt0aGlzLm49cGFyc2VCaWdJbnQoYiwxNik7dGhpcy5lPXBhcnNlSW50KGEsMTYpfWVsc2V7dGhyb3dcIkludmFsaWQgUlNBIHB1YmxpYyBrZXlcIn19fWZ1bmN0aW9uIFJTQURvUHVibGljKGEpe3JldHVybiBhLm1vZFBvd0ludCh0aGlzLmUsdGhpcy5uKX1mdW5jdGlvbiBSU0FFbmNyeXB0KGQpe3ZhciBhPXBrY3MxcGFkMihkLCh0aGlzLm4uYml0TGVuZ3RoKCkrNyk+PjMpO2lmKGE9PW51bGwpe3JldHVybiBudWxsfXZhciBlPXRoaXMuZG9QdWJsaWMoYSk7aWYoZT09bnVsbCl7cmV0dXJuIG51bGx9dmFyIGI9ZS50b1N0cmluZygxNik7aWYoKGIubGVuZ3RoJjEpPT0wKXtyZXR1cm4gYn1lbHNle3JldHVyblwiMFwiK2J9fWZ1bmN0aW9uIFJTQUVuY3J5cHRPQUVQKGYsZSxiKXt2YXIgYT1vYWVwX3BhZChmLCh0aGlzLm4uYml0TGVuZ3RoKCkrNyk+PjMsZSxiKTtpZihhPT1udWxsKXtyZXR1cm4gbnVsbH12YXIgZz10aGlzLmRvUHVibGljKGEpO2lmKGc9PW51bGwpe3JldHVybiBudWxsfXZhciBkPWcudG9TdHJpbmcoMTYpO2lmKChkLmxlbmd0aCYxKT09MCl7cmV0dXJuIGR9ZWxzZXtyZXR1cm5cIjBcIitkfX1SU0FLZXkucHJvdG90eXBlLmRvUHVibGljPVJTQURvUHVibGljO1JTQUtleS5wcm90b3R5cGUuc2V0UHVibGljPVJTQVNldFB1YmxpYztSU0FLZXkucHJvdG90eXBlLmVuY3J5cHQ9UlNBRW5jcnlwdDtSU0FLZXkucHJvdG90eXBlLmVuY3J5cHRPQUVQPVJTQUVuY3J5cHRPQUVQO1JTQUtleS5wcm90b3R5cGUudHlwZT1cIlJTQVwiO1xuLyohIChjKSBUb20gV3UgfCBodHRwOi8vd3d3LWNzLXN0dWRlbnRzLnN0YW5mb3JkLmVkdS9+dGp3L2pzYm4vXHJcbiAqL1xyXG5mdW5jdGlvbiBFQ0ZpZWxkRWxlbWVudEZwKGIsYSl7dGhpcy54PWE7dGhpcy5xPWJ9ZnVuY3Rpb24gZmVGcEVxdWFscyhhKXtpZihhPT10aGlzKXtyZXR1cm4gdHJ1ZX1yZXR1cm4odGhpcy5xLmVxdWFscyhhLnEpJiZ0aGlzLnguZXF1YWxzKGEueCkpfWZ1bmN0aW9uIGZlRnBUb0JpZ0ludGVnZXIoKXtyZXR1cm4gdGhpcy54fWZ1bmN0aW9uIGZlRnBOZWdhdGUoKXtyZXR1cm4gbmV3IEVDRmllbGRFbGVtZW50RnAodGhpcy5xLHRoaXMueC5uZWdhdGUoKS5tb2QodGhpcy5xKSl9ZnVuY3Rpb24gZmVGcEFkZChhKXtyZXR1cm4gbmV3IEVDRmllbGRFbGVtZW50RnAodGhpcy5xLHRoaXMueC5hZGQoYS50b0JpZ0ludGVnZXIoKSkubW9kKHRoaXMucSkpfWZ1bmN0aW9uIGZlRnBTdWJ0cmFjdChhKXtyZXR1cm4gbmV3IEVDRmllbGRFbGVtZW50RnAodGhpcy5xLHRoaXMueC5zdWJ0cmFjdChhLnRvQmlnSW50ZWdlcigpKS5tb2QodGhpcy5xKSl9ZnVuY3Rpb24gZmVGcE11bHRpcGx5KGEpe3JldHVybiBuZXcgRUNGaWVsZEVsZW1lbnRGcCh0aGlzLnEsdGhpcy54Lm11bHRpcGx5KGEudG9CaWdJbnRlZ2VyKCkpLm1vZCh0aGlzLnEpKX1mdW5jdGlvbiBmZUZwU3F1YXJlKCl7cmV0dXJuIG5ldyBFQ0ZpZWxkRWxlbWVudEZwKHRoaXMucSx0aGlzLnguc3F1YXJlKCkubW9kKHRoaXMucSkpfWZ1bmN0aW9uIGZlRnBEaXZpZGUoYSl7cmV0dXJuIG5ldyBFQ0ZpZWxkRWxlbWVudEZwKHRoaXMucSx0aGlzLngubXVsdGlwbHkoYS50b0JpZ0ludGVnZXIoKS5tb2RJbnZlcnNlKHRoaXMucSkpLm1vZCh0aGlzLnEpKX1FQ0ZpZWxkRWxlbWVudEZwLnByb3RvdHlwZS5lcXVhbHM9ZmVGcEVxdWFscztFQ0ZpZWxkRWxlbWVudEZwLnByb3RvdHlwZS50b0JpZ0ludGVnZXI9ZmVGcFRvQmlnSW50ZWdlcjtFQ0ZpZWxkRWxlbWVudEZwLnByb3RvdHlwZS5uZWdhdGU9ZmVGcE5lZ2F0ZTtFQ0ZpZWxkRWxlbWVudEZwLnByb3RvdHlwZS5hZGQ9ZmVGcEFkZDtFQ0ZpZWxkRWxlbWVudEZwLnByb3RvdHlwZS5zdWJ0cmFjdD1mZUZwU3VidHJhY3Q7RUNGaWVsZEVsZW1lbnRGcC5wcm90b3R5cGUubXVsdGlwbHk9ZmVGcE11bHRpcGx5O0VDRmllbGRFbGVtZW50RnAucHJvdG90eXBlLnNxdWFyZT1mZUZwU3F1YXJlO0VDRmllbGRFbGVtZW50RnAucHJvdG90eXBlLmRpdmlkZT1mZUZwRGl2aWRlO2Z1bmN0aW9uIEVDUG9pbnRGcChjLGEsZCxiKXt0aGlzLmN1cnZlPWM7dGhpcy54PWE7dGhpcy55PWQ7aWYoYj09bnVsbCl7dGhpcy56PUJpZ0ludGVnZXIuT05FfWVsc2V7dGhpcy56PWJ9dGhpcy56aW52PW51bGx9ZnVuY3Rpb24gcG9pbnRGcEdldFgoKXtpZih0aGlzLnppbnY9PW51bGwpe3RoaXMuemludj10aGlzLnoubW9kSW52ZXJzZSh0aGlzLmN1cnZlLnEpfXJldHVybiB0aGlzLmN1cnZlLmZyb21CaWdJbnRlZ2VyKHRoaXMueC50b0JpZ0ludGVnZXIoKS5tdWx0aXBseSh0aGlzLnppbnYpLm1vZCh0aGlzLmN1cnZlLnEpKX1mdW5jdGlvbiBwb2ludEZwR2V0WSgpe2lmKHRoaXMuemludj09bnVsbCl7dGhpcy56aW52PXRoaXMuei5tb2RJbnZlcnNlKHRoaXMuY3VydmUucSl9cmV0dXJuIHRoaXMuY3VydmUuZnJvbUJpZ0ludGVnZXIodGhpcy55LnRvQmlnSW50ZWdlcigpLm11bHRpcGx5KHRoaXMuemludikubW9kKHRoaXMuY3VydmUucSkpfWZ1bmN0aW9uIHBvaW50RnBFcXVhbHMoYSl7aWYoYT09dGhpcyl7cmV0dXJuIHRydWV9aWYodGhpcy5pc0luZmluaXR5KCkpe3JldHVybiBhLmlzSW5maW5pdHkoKX1pZihhLmlzSW5maW5pdHkoKSl7cmV0dXJuIHRoaXMuaXNJbmZpbml0eSgpfXZhciBjLGI7Yz1hLnkudG9CaWdJbnRlZ2VyKCkubXVsdGlwbHkodGhpcy56KS5zdWJ0cmFjdCh0aGlzLnkudG9CaWdJbnRlZ2VyKCkubXVsdGlwbHkoYS56KSkubW9kKHRoaXMuY3VydmUucSk7aWYoIWMuZXF1YWxzKEJpZ0ludGVnZXIuWkVSTykpe3JldHVybiBmYWxzZX1iPWEueC50b0JpZ0ludGVnZXIoKS5tdWx0aXBseSh0aGlzLnopLnN1YnRyYWN0KHRoaXMueC50b0JpZ0ludGVnZXIoKS5tdWx0aXBseShhLnopKS5tb2QodGhpcy5jdXJ2ZS5xKTtyZXR1cm4gYi5lcXVhbHMoQmlnSW50ZWdlci5aRVJPKX1mdW5jdGlvbiBwb2ludEZwSXNJbmZpbml0eSgpe2lmKCh0aGlzLng9PW51bGwpJiYodGhpcy55PT1udWxsKSl7cmV0dXJuIHRydWV9cmV0dXJuIHRoaXMuei5lcXVhbHMoQmlnSW50ZWdlci5aRVJPKSYmIXRoaXMueS50b0JpZ0ludGVnZXIoKS5lcXVhbHMoQmlnSW50ZWdlci5aRVJPKX1mdW5jdGlvbiBwb2ludEZwTmVnYXRlKCl7cmV0dXJuIG5ldyBFQ1BvaW50RnAodGhpcy5jdXJ2ZSx0aGlzLngsdGhpcy55Lm5lZ2F0ZSgpLHRoaXMueil9ZnVuY3Rpb24gcG9pbnRGcEFkZChsKXtpZih0aGlzLmlzSW5maW5pdHkoKSl7cmV0dXJuIGx9aWYobC5pc0luZmluaXR5KCkpe3JldHVybiB0aGlzfXZhciBwPWwueS50b0JpZ0ludGVnZXIoKS5tdWx0aXBseSh0aGlzLnopLnN1YnRyYWN0KHRoaXMueS50b0JpZ0ludGVnZXIoKS5tdWx0aXBseShsLnopKS5tb2QodGhpcy5jdXJ2ZS5xKTt2YXIgbz1sLngudG9CaWdJbnRlZ2VyKCkubXVsdGlwbHkodGhpcy56KS5zdWJ0cmFjdCh0aGlzLngudG9CaWdJbnRlZ2VyKCkubXVsdGlwbHkobC56KSkubW9kKHRoaXMuY3VydmUucSk7aWYoQmlnSW50ZWdlci5aRVJPLmVxdWFscyhvKSl7aWYoQmlnSW50ZWdlci5aRVJPLmVxdWFscyhwKSl7cmV0dXJuIHRoaXMudHdpY2UoKX1yZXR1cm4gdGhpcy5jdXJ2ZS5nZXRJbmZpbml0eSgpfXZhciBqPW5ldyBCaWdJbnRlZ2VyKFwiM1wiKTt2YXIgZT10aGlzLngudG9CaWdJbnRlZ2VyKCk7dmFyIG49dGhpcy55LnRvQmlnSW50ZWdlcigpO3ZhciBjPWwueC50b0JpZ0ludGVnZXIoKTt2YXIgaz1sLnkudG9CaWdJbnRlZ2VyKCk7dmFyIG09by5zcXVhcmUoKTt2YXIgaT1tLm11bHRpcGx5KG8pO3ZhciBkPWUubXVsdGlwbHkobSk7dmFyIGc9cC5zcXVhcmUoKS5tdWx0aXBseSh0aGlzLnopO3ZhciBhPWcuc3VidHJhY3QoZC5zaGlmdExlZnQoMSkpLm11bHRpcGx5KGwueikuc3VidHJhY3QoaSkubXVsdGlwbHkobykubW9kKHRoaXMuY3VydmUucSk7dmFyIGg9ZC5tdWx0aXBseShqKS5tdWx0aXBseShwKS5zdWJ0cmFjdChuLm11bHRpcGx5KGkpKS5zdWJ0cmFjdChnLm11bHRpcGx5KHApKS5tdWx0aXBseShsLnopLmFkZChwLm11bHRpcGx5KGkpKS5tb2QodGhpcy5jdXJ2ZS5xKTt2YXIgZj1pLm11bHRpcGx5KHRoaXMueikubXVsdGlwbHkobC56KS5tb2QodGhpcy5jdXJ2ZS5xKTtyZXR1cm4gbmV3IEVDUG9pbnRGcCh0aGlzLmN1cnZlLHRoaXMuY3VydmUuZnJvbUJpZ0ludGVnZXIoYSksdGhpcy5jdXJ2ZS5mcm9tQmlnSW50ZWdlcihoKSxmKX1mdW5jdGlvbiBwb2ludEZwVHdpY2UoKXtpZih0aGlzLmlzSW5maW5pdHkoKSl7cmV0dXJuIHRoaXN9aWYodGhpcy55LnRvQmlnSW50ZWdlcigpLnNpZ251bSgpPT0wKXtyZXR1cm4gdGhpcy5jdXJ2ZS5nZXRJbmZpbml0eSgpfXZhciBnPW5ldyBCaWdJbnRlZ2VyKFwiM1wiKTt2YXIgYz10aGlzLngudG9CaWdJbnRlZ2VyKCk7dmFyIGg9dGhpcy55LnRvQmlnSW50ZWdlcigpO3ZhciBlPWgubXVsdGlwbHkodGhpcy56KTt2YXIgaj1lLm11bHRpcGx5KGgpLm1vZCh0aGlzLmN1cnZlLnEpO3ZhciBpPXRoaXMuY3VydmUuYS50b0JpZ0ludGVnZXIoKTt2YXIgaz1jLnNxdWFyZSgpLm11bHRpcGx5KGcpO2lmKCFCaWdJbnRlZ2VyLlpFUk8uZXF1YWxzKGkpKXtrPWsuYWRkKHRoaXMuei5zcXVhcmUoKS5tdWx0aXBseShpKSl9az1rLm1vZCh0aGlzLmN1cnZlLnEpO3ZhciBiPWsuc3F1YXJlKCkuc3VidHJhY3QoYy5zaGlmdExlZnQoMykubXVsdGlwbHkoaikpLnNoaWZ0TGVmdCgxKS5tdWx0aXBseShlKS5tb2QodGhpcy5jdXJ2ZS5xKTt2YXIgZj1rLm11bHRpcGx5KGcpLm11bHRpcGx5KGMpLnN1YnRyYWN0KGouc2hpZnRMZWZ0KDEpKS5zaGlmdExlZnQoMikubXVsdGlwbHkoaikuc3VidHJhY3Qoay5zcXVhcmUoKS5tdWx0aXBseShrKSkubW9kKHRoaXMuY3VydmUucSk7dmFyIGQ9ZS5zcXVhcmUoKS5tdWx0aXBseShlKS5zaGlmdExlZnQoMykubW9kKHRoaXMuY3VydmUucSk7cmV0dXJuIG5ldyBFQ1BvaW50RnAodGhpcy5jdXJ2ZSx0aGlzLmN1cnZlLmZyb21CaWdJbnRlZ2VyKGIpLHRoaXMuY3VydmUuZnJvbUJpZ0ludGVnZXIoZiksZCl9ZnVuY3Rpb24gcG9pbnRGcE11bHRpcGx5KGIpe2lmKHRoaXMuaXNJbmZpbml0eSgpKXtyZXR1cm4gdGhpc31pZihiLnNpZ251bSgpPT0wKXtyZXR1cm4gdGhpcy5jdXJ2ZS5nZXRJbmZpbml0eSgpfXZhciBnPWI7dmFyIGY9Zy5tdWx0aXBseShuZXcgQmlnSW50ZWdlcihcIjNcIikpO3ZhciBsPXRoaXMubmVnYXRlKCk7dmFyIGQ9dGhpczt2YXIgYztmb3IoYz1mLmJpdExlbmd0aCgpLTI7Yz4wOy0tYyl7ZD1kLnR3aWNlKCk7dmFyIGE9Zi50ZXN0Qml0KGMpO3ZhciBqPWcudGVzdEJpdChjKTtpZihhIT1qKXtkPWQuYWRkKGE/dGhpczpsKX19cmV0dXJuIGR9ZnVuY3Rpb24gcG9pbnRGcE11bHRpcGx5VHdvKGMsYSxiKXt2YXIgZDtpZihjLmJpdExlbmd0aCgpPmIuYml0TGVuZ3RoKCkpe2Q9Yy5iaXRMZW5ndGgoKS0xfWVsc2V7ZD1iLmJpdExlbmd0aCgpLTF9dmFyIGY9dGhpcy5jdXJ2ZS5nZXRJbmZpbml0eSgpO3ZhciBlPXRoaXMuYWRkKGEpO3doaWxlKGQ+PTApe2Y9Zi50d2ljZSgpO2lmKGMudGVzdEJpdChkKSl7aWYoYi50ZXN0Qml0KGQpKXtmPWYuYWRkKGUpfWVsc2V7Zj1mLmFkZCh0aGlzKX19ZWxzZXtpZihiLnRlc3RCaXQoZCkpe2Y9Zi5hZGQoYSl9fS0tZH1yZXR1cm4gZn1FQ1BvaW50RnAucHJvdG90eXBlLmdldFg9cG9pbnRGcEdldFg7RUNQb2ludEZwLnByb3RvdHlwZS5nZXRZPXBvaW50RnBHZXRZO0VDUG9pbnRGcC5wcm90b3R5cGUuZXF1YWxzPXBvaW50RnBFcXVhbHM7RUNQb2ludEZwLnByb3RvdHlwZS5pc0luZmluaXR5PXBvaW50RnBJc0luZmluaXR5O0VDUG9pbnRGcC5wcm90b3R5cGUubmVnYXRlPXBvaW50RnBOZWdhdGU7RUNQb2ludEZwLnByb3RvdHlwZS5hZGQ9cG9pbnRGcEFkZDtFQ1BvaW50RnAucHJvdG90eXBlLnR3aWNlPXBvaW50RnBUd2ljZTtFQ1BvaW50RnAucHJvdG90eXBlLm11bHRpcGx5PXBvaW50RnBNdWx0aXBseTtFQ1BvaW50RnAucHJvdG90eXBlLm11bHRpcGx5VHdvPXBvaW50RnBNdWx0aXBseVR3bztmdW5jdGlvbiBFQ0N1cnZlRnAoZSxkLGMpe3RoaXMucT1lO3RoaXMuYT10aGlzLmZyb21CaWdJbnRlZ2VyKGQpO3RoaXMuYj10aGlzLmZyb21CaWdJbnRlZ2VyKGMpO3RoaXMuaW5maW5pdHk9bmV3IEVDUG9pbnRGcCh0aGlzLG51bGwsbnVsbCl9ZnVuY3Rpb24gY3VydmVGcEdldFEoKXtyZXR1cm4gdGhpcy5xfWZ1bmN0aW9uIGN1cnZlRnBHZXRBKCl7cmV0dXJuIHRoaXMuYX1mdW5jdGlvbiBjdXJ2ZUZwR2V0Qigpe3JldHVybiB0aGlzLmJ9ZnVuY3Rpb24gY3VydmVGcEVxdWFscyhhKXtpZihhPT10aGlzKXtyZXR1cm4gdHJ1ZX1yZXR1cm4odGhpcy5xLmVxdWFscyhhLnEpJiZ0aGlzLmEuZXF1YWxzKGEuYSkmJnRoaXMuYi5lcXVhbHMoYS5iKSl9ZnVuY3Rpb24gY3VydmVGcEdldEluZmluaXR5KCl7cmV0dXJuIHRoaXMuaW5maW5pdHl9ZnVuY3Rpb24gY3VydmVGcEZyb21CaWdJbnRlZ2VyKGEpe3JldHVybiBuZXcgRUNGaWVsZEVsZW1lbnRGcCh0aGlzLnEsYSl9ZnVuY3Rpb24gY3VydmVGcERlY29kZVBvaW50SGV4KGQpe3N3aXRjaChwYXJzZUludChkLnN1YnN0cigwLDIpLDE2KSl7Y2FzZSAwOnJldHVybiB0aGlzLmluZmluaXR5O2Nhc2UgMjpjYXNlIDM6cmV0dXJuIG51bGw7Y2FzZSA0OmNhc2UgNjpjYXNlIDc6dmFyIGE9KGQubGVuZ3RoLTIpLzI7dmFyIGM9ZC5zdWJzdHIoMixhKTt2YXIgYj1kLnN1YnN0cihhKzIsYSk7cmV0dXJuIG5ldyBFQ1BvaW50RnAodGhpcyx0aGlzLmZyb21CaWdJbnRlZ2VyKG5ldyBCaWdJbnRlZ2VyKGMsMTYpKSx0aGlzLmZyb21CaWdJbnRlZ2VyKG5ldyBCaWdJbnRlZ2VyKGIsMTYpKSk7ZGVmYXVsdDpyZXR1cm4gbnVsbH19RUNDdXJ2ZUZwLnByb3RvdHlwZS5nZXRRPWN1cnZlRnBHZXRRO0VDQ3VydmVGcC5wcm90b3R5cGUuZ2V0QT1jdXJ2ZUZwR2V0QTtFQ0N1cnZlRnAucHJvdG90eXBlLmdldEI9Y3VydmVGcEdldEI7RUNDdXJ2ZUZwLnByb3RvdHlwZS5lcXVhbHM9Y3VydmVGcEVxdWFscztFQ0N1cnZlRnAucHJvdG90eXBlLmdldEluZmluaXR5PWN1cnZlRnBHZXRJbmZpbml0eTtFQ0N1cnZlRnAucHJvdG90eXBlLmZyb21CaWdJbnRlZ2VyPWN1cnZlRnBGcm9tQmlnSW50ZWdlcjtFQ0N1cnZlRnAucHJvdG90eXBlLmRlY29kZVBvaW50SGV4PWN1cnZlRnBEZWNvZGVQb2ludEhleDtcbi8qISAoYykgU3RlZmFuIFRob21hcyB8IGh0dHBzOi8vZ2l0aHViLmNvbS9iaXRjb2luanMvYml0Y29pbmpzLWxpYlxyXG4gKi9cclxuRUNGaWVsZEVsZW1lbnRGcC5wcm90b3R5cGUuZ2V0Qnl0ZUxlbmd0aD1mdW5jdGlvbigpe3JldHVybiBNYXRoLmZsb29yKCh0aGlzLnRvQmlnSW50ZWdlcigpLmJpdExlbmd0aCgpKzcpLzgpfTtFQ1BvaW50RnAucHJvdG90eXBlLmdldEVuY29kZWQ9ZnVuY3Rpb24oYyl7dmFyIGQ9ZnVuY3Rpb24oaCxmKXt2YXIgZz1oLnRvQnl0ZUFycmF5VW5zaWduZWQoKTtpZihmPGcubGVuZ3RoKXtnPWcuc2xpY2UoZy5sZW5ndGgtZil9ZWxzZXt3aGlsZShmPmcubGVuZ3RoKXtnLnVuc2hpZnQoMCl9fXJldHVybiBnfTt2YXIgYT10aGlzLmdldFgoKS50b0JpZ0ludGVnZXIoKTt2YXIgZT10aGlzLmdldFkoKS50b0JpZ0ludGVnZXIoKTt2YXIgYj1kKGEsMzIpO2lmKGMpe2lmKGUuaXNFdmVuKCkpe2IudW5zaGlmdCgyKX1lbHNle2IudW5zaGlmdCgzKX19ZWxzZXtiLnVuc2hpZnQoNCk7Yj1iLmNvbmNhdChkKGUsMzIpKX1yZXR1cm4gYn07RUNQb2ludEZwLmRlY29kZUZyb209ZnVuY3Rpb24oZyxjKXt2YXIgZj1jWzBdO3ZhciBlPWMubGVuZ3RoLTE7dmFyIGQ9Yy5zbGljZSgxLDErZS8yKTt2YXIgYj1jLnNsaWNlKDErZS8yLDErZSk7ZC51bnNoaWZ0KDApO2IudW5zaGlmdCgwKTt2YXIgYT1uZXcgQmlnSW50ZWdlcihkKTt2YXIgaD1uZXcgQmlnSW50ZWdlcihiKTtyZXR1cm4gbmV3IEVDUG9pbnRGcChnLGcuZnJvbUJpZ0ludGVnZXIoYSksZy5mcm9tQmlnSW50ZWdlcihoKSl9O0VDUG9pbnRGcC5kZWNvZGVGcm9tSGV4PWZ1bmN0aW9uKGcsYyl7dmFyIGY9Yy5zdWJzdHIoMCwyKTt2YXIgZT1jLmxlbmd0aC0yO3ZhciBkPWMuc3Vic3RyKDIsZS8yKTt2YXIgYj1jLnN1YnN0cigyK2UvMixlLzIpO3ZhciBhPW5ldyBCaWdJbnRlZ2VyKGQsMTYpO3ZhciBoPW5ldyBCaWdJbnRlZ2VyKGIsMTYpO3JldHVybiBuZXcgRUNQb2ludEZwKGcsZy5mcm9tQmlnSW50ZWdlcihhKSxnLmZyb21CaWdJbnRlZ2VyKGgpKX07RUNQb2ludEZwLnByb3RvdHlwZS5hZGQyRD1mdW5jdGlvbihjKXtpZih0aGlzLmlzSW5maW5pdHkoKSl7cmV0dXJuIGN9aWYoYy5pc0luZmluaXR5KCkpe3JldHVybiB0aGlzfWlmKHRoaXMueC5lcXVhbHMoYy54KSl7aWYodGhpcy55LmVxdWFscyhjLnkpKXtyZXR1cm4gdGhpcy50d2ljZSgpfXJldHVybiB0aGlzLmN1cnZlLmdldEluZmluaXR5KCl9dmFyIGc9Yy54LnN1YnRyYWN0KHRoaXMueCk7dmFyIGU9Yy55LnN1YnRyYWN0KHRoaXMueSk7dmFyIGE9ZS5kaXZpZGUoZyk7dmFyIGQ9YS5zcXVhcmUoKS5zdWJ0cmFjdCh0aGlzLngpLnN1YnRyYWN0KGMueCk7dmFyIGY9YS5tdWx0aXBseSh0aGlzLnguc3VidHJhY3QoZCkpLnN1YnRyYWN0KHRoaXMueSk7cmV0dXJuIG5ldyBFQ1BvaW50RnAodGhpcy5jdXJ2ZSxkLGYpfTtFQ1BvaW50RnAucHJvdG90eXBlLnR3aWNlMkQ9ZnVuY3Rpb24oKXtpZih0aGlzLmlzSW5maW5pdHkoKSl7cmV0dXJuIHRoaXN9aWYodGhpcy55LnRvQmlnSW50ZWdlcigpLnNpZ251bSgpPT0wKXtyZXR1cm4gdGhpcy5jdXJ2ZS5nZXRJbmZpbml0eSgpfXZhciBiPXRoaXMuY3VydmUuZnJvbUJpZ0ludGVnZXIoQmlnSW50ZWdlci52YWx1ZU9mKDIpKTt2YXIgZT10aGlzLmN1cnZlLmZyb21CaWdJbnRlZ2VyKEJpZ0ludGVnZXIudmFsdWVPZigzKSk7dmFyIGE9dGhpcy54LnNxdWFyZSgpLm11bHRpcGx5KGUpLmFkZCh0aGlzLmN1cnZlLmEpLmRpdmlkZSh0aGlzLnkubXVsdGlwbHkoYikpO3ZhciBjPWEuc3F1YXJlKCkuc3VidHJhY3QodGhpcy54Lm11bHRpcGx5KGIpKTt2YXIgZD1hLm11bHRpcGx5KHRoaXMueC5zdWJ0cmFjdChjKSkuc3VidHJhY3QodGhpcy55KTtyZXR1cm4gbmV3IEVDUG9pbnRGcCh0aGlzLmN1cnZlLGMsZCl9O0VDUG9pbnRGcC5wcm90b3R5cGUubXVsdGlwbHkyRD1mdW5jdGlvbihiKXtpZih0aGlzLmlzSW5maW5pdHkoKSl7cmV0dXJuIHRoaXN9aWYoYi5zaWdudW0oKT09MCl7cmV0dXJuIHRoaXMuY3VydmUuZ2V0SW5maW5pdHkoKX12YXIgZz1iO3ZhciBmPWcubXVsdGlwbHkobmV3IEJpZ0ludGVnZXIoXCIzXCIpKTt2YXIgbD10aGlzLm5lZ2F0ZSgpO3ZhciBkPXRoaXM7dmFyIGM7Zm9yKGM9Zi5iaXRMZW5ndGgoKS0yO2M+MDstLWMpe2Q9ZC50d2ljZSgpO3ZhciBhPWYudGVzdEJpdChjKTt2YXIgaj1nLnRlc3RCaXQoYyk7aWYoYSE9ail7ZD1kLmFkZDJEKGE/dGhpczpsKX19cmV0dXJuIGR9O0VDUG9pbnRGcC5wcm90b3R5cGUuaXNPbkN1cnZlPWZ1bmN0aW9uKCl7dmFyIGQ9dGhpcy5nZXRYKCkudG9CaWdJbnRlZ2VyKCk7dmFyIGk9dGhpcy5nZXRZKCkudG9CaWdJbnRlZ2VyKCk7dmFyIGY9dGhpcy5jdXJ2ZS5nZXRBKCkudG9CaWdJbnRlZ2VyKCk7dmFyIGM9dGhpcy5jdXJ2ZS5nZXRCKCkudG9CaWdJbnRlZ2VyKCk7dmFyIGg9dGhpcy5jdXJ2ZS5nZXRRKCk7dmFyIGU9aS5tdWx0aXBseShpKS5tb2QoaCk7dmFyIGc9ZC5tdWx0aXBseShkKS5tdWx0aXBseShkKS5hZGQoZi5tdWx0aXBseShkKSkuYWRkKGMpLm1vZChoKTtyZXR1cm4gZS5lcXVhbHMoZyl9O0VDUG9pbnRGcC5wcm90b3R5cGUudG9TdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm5cIihcIit0aGlzLmdldFgoKS50b0JpZ0ludGVnZXIoKS50b1N0cmluZygpK1wiLFwiK3RoaXMuZ2V0WSgpLnRvQmlnSW50ZWdlcigpLnRvU3RyaW5nKCkrXCIpXCJ9O0VDUG9pbnRGcC5wcm90b3R5cGUudmFsaWRhdGU9ZnVuY3Rpb24oKXt2YXIgYz10aGlzLmN1cnZlLmdldFEoKTtpZih0aGlzLmlzSW5maW5pdHkoKSl7dGhyb3cgbmV3IEVycm9yKFwiUG9pbnQgaXMgYXQgaW5maW5pdHkuXCIpfXZhciBhPXRoaXMuZ2V0WCgpLnRvQmlnSW50ZWdlcigpO3ZhciBiPXRoaXMuZ2V0WSgpLnRvQmlnSW50ZWdlcigpO2lmKGEuY29tcGFyZVRvKEJpZ0ludGVnZXIuT05FKTwwfHxhLmNvbXBhcmVUbyhjLnN1YnRyYWN0KEJpZ0ludGVnZXIuT05FKSk+MCl7dGhyb3cgbmV3IEVycm9yKFwieCBjb29yZGluYXRlIG91dCBvZiBib3VuZHNcIil9aWYoYi5jb21wYXJlVG8oQmlnSW50ZWdlci5PTkUpPDB8fGIuY29tcGFyZVRvKGMuc3VidHJhY3QoQmlnSW50ZWdlci5PTkUpKT4wKXt0aHJvdyBuZXcgRXJyb3IoXCJ5IGNvb3JkaW5hdGUgb3V0IG9mIGJvdW5kc1wiKX1pZighdGhpcy5pc09uQ3VydmUoKSl7dGhyb3cgbmV3IEVycm9yKFwiUG9pbnQgaXMgbm90IG9uIHRoZSBjdXJ2ZS5cIil9aWYodGhpcy5tdWx0aXBseShjKS5pc0luZmluaXR5KCkpe3Rocm93IG5ldyBFcnJvcihcIlBvaW50IGlzIG5vdCBhIHNjYWxhciBtdWx0aXBsZSBvZiBHLlwiKX1yZXR1cm4gdHJ1ZX07XG4vKiEgTWlrZSBTYW11ZWwgKGMpIDIwMDkgfCBjb2RlLmdvb2dsZS5jb20vcC9qc29uLXNhbnMtZXZhbFxyXG4gKi9cclxudmFyIGpzb25QYXJzZT0oZnVuY3Rpb24oKXt2YXIgZT1cIig/Oi0/XFxcXGIoPzowfFsxLTldWzAtOV0qKSg/OlxcXFwuWzAtOV0rKT8oPzpbZUVdWystXT9bMC05XSspP1xcXFxiKVwiO3ZhciBqPScoPzpbXlxcXFwwLVxcXFx4MDhcXFxceDBhLVxcXFx4MWZcIlxcXFxcXFxcXXxcXFxcXFxcXCg/OltcIi9cXFxcXFxcXGJmbnJ0XXx1WzAtOUEtRmEtZl17NH0pKSc7dmFyIGk9Jyg/OlwiJytqKycqXCIpJzt2YXIgZD1uZXcgUmVnRXhwKFwiKD86ZmFsc2V8dHJ1ZXxudWxsfFtcXFxce1xcXFx9XFxcXFtcXFxcXV18XCIrZStcInxcIitpK1wiKVwiLFwiZ1wiKTt2YXIgaz1uZXcgUmVnRXhwKFwiXFxcXFxcXFwoPzooW151XSl8dSguezR9KSlcIixcImdcIik7dmFyIGc9eydcIic6J1wiJyxcIi9cIjpcIi9cIixcIlxcXFxcIjpcIlxcXFxcIixiOlwiXFxiXCIsZjpcIlxcZlwiLG46XCJcXG5cIixyOlwiXFxyXCIsdDpcIlxcdFwifTtmdW5jdGlvbiBoKGwsbSxuKXtyZXR1cm4gbT9nW21dOlN0cmluZy5mcm9tQ2hhckNvZGUocGFyc2VJbnQobiwxNikpfXZhciBjPW5ldyBTdHJpbmcoXCJcIik7dmFyIGE9XCJcXFxcXCI7dmFyIGY9e1wie1wiOk9iamVjdCxcIltcIjpBcnJheX07dmFyIGI9T2JqZWN0Lmhhc093blByb3BlcnR5O3JldHVybiBmdW5jdGlvbih1LHEpe3ZhciBwPXUubWF0Y2goZCk7dmFyIHg7dmFyIHY9cFswXTt2YXIgbD1mYWxzZTtpZihcIntcIj09PXYpe3g9e319ZWxzZXtpZihcIltcIj09PXYpe3g9W119ZWxzZXt4PVtdO2w9dHJ1ZX19dmFyIHQ7dmFyIHI9W3hdO2Zvcih2YXIgbz0xLWwsbT1wLmxlbmd0aDtvPG07KytvKXt2PXBbb107dmFyIHc7c3dpdGNoKHYuY2hhckNvZGVBdCgwKSl7ZGVmYXVsdDp3PXJbMF07d1t0fHx3Lmxlbmd0aF09Kyh2KTt0PXZvaWQgMDticmVhaztjYXNlIDM0OnY9di5zdWJzdHJpbmcoMSx2Lmxlbmd0aC0xKTtpZih2LmluZGV4T2YoYSkhPT0tMSl7dj12LnJlcGxhY2UoayxoKX13PXJbMF07aWYoIXQpe2lmKHcgaW5zdGFuY2VvZiBBcnJheSl7dD13Lmxlbmd0aH1lbHNle3Q9dnx8YzticmVha319d1t0XT12O3Q9dm9pZCAwO2JyZWFrO2Nhc2UgOTE6dz1yWzBdO3IudW5zaGlmdCh3W3R8fHcubGVuZ3RoXT1bXSk7dD12b2lkIDA7YnJlYWs7Y2FzZSA5MzpyLnNoaWZ0KCk7YnJlYWs7Y2FzZSAxMDI6dz1yWzBdO3dbdHx8dy5sZW5ndGhdPWZhbHNlO3Q9dm9pZCAwO2JyZWFrO2Nhc2UgMTEwOnc9clswXTt3W3R8fHcubGVuZ3RoXT1udWxsO3Q9dm9pZCAwO2JyZWFrO2Nhc2UgMTE2Onc9clswXTt3W3R8fHcubGVuZ3RoXT10cnVlO3Q9dm9pZCAwO2JyZWFrO2Nhc2UgMTIzOnc9clswXTtyLnVuc2hpZnQod1t0fHx3Lmxlbmd0aF09e30pO3Q9dm9pZCAwO2JyZWFrO2Nhc2UgMTI1OnIuc2hpZnQoKTticmVha319aWYobCl7aWYoci5sZW5ndGghPT0xKXt0aHJvdyBuZXcgRXJyb3IoKX14PXhbMF19ZWxzZXtpZihyLmxlbmd0aCl7dGhyb3cgbmV3IEVycm9yKCl9fWlmKHEpe3ZhciBzPWZ1bmN0aW9uKEMsQil7dmFyIEQ9Q1tCXTtpZihEJiZ0eXBlb2YgRD09PVwib2JqZWN0XCIpe3ZhciBuPW51bGw7Zm9yKHZhciB6IGluIEQpe2lmKGIuY2FsbChELHopJiZEIT09Qyl7dmFyIHk9cyhELHopO2lmKHkhPT12b2lkIDApe0Rbel09eX1lbHNle2lmKCFuKXtuPVtdfW4ucHVzaCh6KX19fWlmKG4pe2Zvcih2YXIgQT1uLmxlbmd0aDstLUE+PTA7KXtkZWxldGUgRFtuW0FdXX19fXJldHVybiBxLmNhbGwoQyxCLEQpfTt4PXMoe1wiXCI6eH0sXCJcIil9cmV0dXJuIHh9fSkoKTtcbmlmKHR5cGVvZiBLSlVSPT1cInVuZGVmaW5lZFwifHwhS0pVUil7S0pVUj17fX1pZih0eXBlb2YgS0pVUi5hc24xPT1cInVuZGVmaW5lZFwifHwhS0pVUi5hc24xKXtLSlVSLmFzbjE9e319S0pVUi5hc24xLkFTTjFVdGlsPW5ldyBmdW5jdGlvbigpe3RoaXMuaW50ZWdlclRvQnl0ZUhleD1mdW5jdGlvbihhKXt2YXIgYj1hLnRvU3RyaW5nKDE2KTtpZigoYi5sZW5ndGglMik9PTEpe2I9XCIwXCIrYn1yZXR1cm4gYn07dGhpcy5iaWdJbnRUb01pblR3b3NDb21wbGVtZW50c0hleD1mdW5jdGlvbihqKXt2YXIgZj1qLnRvU3RyaW5nKDE2KTtpZihmLnN1YnN0cigwLDEpIT1cIi1cIil7aWYoZi5sZW5ndGglMj09MSl7Zj1cIjBcIitmfWVsc2V7aWYoIWYubWF0Y2goL15bMC03XS8pKXtmPVwiMDBcIitmfX19ZWxzZXt2YXIgYT1mLnN1YnN0cigxKTt2YXIgZT1hLmxlbmd0aDtpZihlJTI9PTEpe2UrPTF9ZWxzZXtpZighZi5tYXRjaCgvXlswLTddLykpe2UrPTJ9fXZhciBnPVwiXCI7Zm9yKHZhciBkPTA7ZDxlO2QrKyl7Zys9XCJmXCJ9dmFyIGM9bmV3IEJpZ0ludGVnZXIoZywxNik7dmFyIGI9Yy54b3IoaikuYWRkKEJpZ0ludGVnZXIuT05FKTtmPWIudG9TdHJpbmcoMTYpLnJlcGxhY2UoL14tLyxcIlwiKX1yZXR1cm4gZn07dGhpcy5nZXRQRU1TdHJpbmdGcm9tSGV4PWZ1bmN0aW9uKGEsYil7cmV0dXJuIGhleHRvcGVtKGEsYil9O3RoaXMubmV3T2JqZWN0PWZ1bmN0aW9uKGspe3ZhciBEPUtKVVIsbj1ELmFzbjEsej1uLkRFUkJvb2xlYW4sZT1uLkRFUkludGVnZXIscz1uLkRFUkJpdFN0cmluZyxoPW4uREVST2N0ZXRTdHJpbmcsdj1uLkRFUk51bGwsdz1uLkRFUk9iamVjdElkZW50aWZpZXIsbD1uLkRFUkVudW1lcmF0ZWQsZz1uLkRFUlVURjhTdHJpbmcsZj1uLkRFUk51bWVyaWNTdHJpbmcseT1uLkRFUlByaW50YWJsZVN0cmluZyx1PW4uREVSVGVsZXRleFN0cmluZyxwPW4uREVSSUE1U3RyaW5nLEM9bi5ERVJVVENUaW1lLGo9bi5ERVJHZW5lcmFsaXplZFRpbWUsbT1uLkRFUlNlcXVlbmNlLGM9bi5ERVJTZXQscj1uLkRFUlRhZ2dlZE9iamVjdCxvPW4uQVNOMVV0aWwubmV3T2JqZWN0O3ZhciB0PU9iamVjdC5rZXlzKGspO2lmKHQubGVuZ3RoIT0xKXt0aHJvd1wia2V5IG9mIHBhcmFtIHNoYWxsIGJlIG9ubHkgb25lLlwifXZhciBGPXRbMF07aWYoXCI6Ym9vbDppbnQ6Yml0c3RyOm9jdHN0cjpudWxsOm9pZDplbnVtOnV0ZjhzdHI6bnVtc3RyOnBybnN0cjp0ZWxzdHI6aWE1c3RyOnV0Y3RpbWU6Z2VudGltZTpzZXE6c2V0OnRhZzpcIi5pbmRleE9mKFwiOlwiK0YrXCI6XCIpPT0tMSl7dGhyb3dcInVuZGVmaW5lZCBrZXk6IFwiK0Z9aWYoRj09XCJib29sXCIpe3JldHVybiBuZXcgeihrW0ZdKX1pZihGPT1cImludFwiKXtyZXR1cm4gbmV3IGUoa1tGXSl9aWYoRj09XCJiaXRzdHJcIil7cmV0dXJuIG5ldyBzKGtbRl0pfWlmKEY9PVwib2N0c3RyXCIpe3JldHVybiBuZXcgaChrW0ZdKX1pZihGPT1cIm51bGxcIil7cmV0dXJuIG5ldyB2KGtbRl0pfWlmKEY9PVwib2lkXCIpe3JldHVybiBuZXcgdyhrW0ZdKX1pZihGPT1cImVudW1cIil7cmV0dXJuIG5ldyBsKGtbRl0pfWlmKEY9PVwidXRmOHN0clwiKXtyZXR1cm4gbmV3IGcoa1tGXSl9aWYoRj09XCJudW1zdHJcIil7cmV0dXJuIG5ldyBmKGtbRl0pfWlmKEY9PVwicHJuc3RyXCIpe3JldHVybiBuZXcgeShrW0ZdKX1pZihGPT1cInRlbHN0clwiKXtyZXR1cm4gbmV3IHUoa1tGXSl9aWYoRj09XCJpYTVzdHJcIil7cmV0dXJuIG5ldyBwKGtbRl0pfWlmKEY9PVwidXRjdGltZVwiKXtyZXR1cm4gbmV3IEMoa1tGXSl9aWYoRj09XCJnZW50aW1lXCIpe3JldHVybiBuZXcgaihrW0ZdKX1pZihGPT1cInNlcVwiKXt2YXIgZD1rW0ZdO3ZhciBFPVtdO2Zvcih2YXIgeD0wO3g8ZC5sZW5ndGg7eCsrKXt2YXIgQj1vKGRbeF0pO0UucHVzaChCKX1yZXR1cm4gbmV3IG0oe2FycmF5OkV9KX1pZihGPT1cInNldFwiKXt2YXIgZD1rW0ZdO3ZhciBFPVtdO2Zvcih2YXIgeD0wO3g8ZC5sZW5ndGg7eCsrKXt2YXIgQj1vKGRbeF0pO0UucHVzaChCKX1yZXR1cm4gbmV3IGMoe2FycmF5OkV9KX1pZihGPT1cInRhZ1wiKXt2YXIgQT1rW0ZdO2lmKE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcuY2FsbChBKT09PVwiW29iamVjdCBBcnJheV1cIiYmQS5sZW5ndGg9PTMpe3ZhciBxPW8oQVsyXSk7cmV0dXJuIG5ldyByKHt0YWc6QVswXSxleHBsaWNpdDpBWzFdLG9iajpxfSl9ZWxzZXt2YXIgYj17fTtpZihBLmV4cGxpY2l0IT09dW5kZWZpbmVkKXtiLmV4cGxpY2l0PUEuZXhwbGljaXR9aWYoQS50YWchPT11bmRlZmluZWQpe2IudGFnPUEudGFnfWlmKEEub2JqPT09dW5kZWZpbmVkKXt0aHJvd1wib2JqIHNoYWxsIGJlIHNwZWNpZmllZCBmb3IgJ3RhZycuXCJ9Yi5vYmo9byhBLm9iaik7cmV0dXJuIG5ldyByKGIpfX19O3RoaXMuanNvblRvQVNOMUhFWD1mdW5jdGlvbihiKXt2YXIgYT10aGlzLm5ld09iamVjdChiKTtyZXR1cm4gYS5nZXRFbmNvZGVkSGV4KCl9fTtLSlVSLmFzbjEuQVNOMVV0aWwub2lkSGV4VG9JbnQ9ZnVuY3Rpb24oYSl7dmFyIGo9XCJcIjt2YXIgaz1wYXJzZUludChhLnN1YnN0cigwLDIpLDE2KTt2YXIgZD1NYXRoLmZsb29yKGsvNDApO3ZhciBjPWslNDA7dmFyIGo9ZCtcIi5cIitjO3ZhciBlPVwiXCI7Zm9yKHZhciBmPTI7ZjxhLmxlbmd0aDtmKz0yKXt2YXIgZz1wYXJzZUludChhLnN1YnN0cihmLDIpLDE2KTt2YXIgaD0oXCIwMDAwMDAwMFwiK2cudG9TdHJpbmcoMikpLnNsaWNlKC04KTtlPWUraC5zdWJzdHIoMSw3KTtpZihoLnN1YnN0cigwLDEpPT1cIjBcIil7dmFyIGI9bmV3IEJpZ0ludGVnZXIoZSwyKTtqPWorXCIuXCIrYi50b1N0cmluZygxMCk7ZT1cIlwifX1yZXR1cm4gan07S0pVUi5hc24xLkFTTjFVdGlsLm9pZEludFRvSGV4PWZ1bmN0aW9uKGYpe3ZhciBlPWZ1bmN0aW9uKGEpe3ZhciBrPWEudG9TdHJpbmcoMTYpO2lmKGsubGVuZ3RoPT0xKXtrPVwiMFwiK2t9cmV0dXJuIGt9O3ZhciBkPWZ1bmN0aW9uKG8pe3ZhciBuPVwiXCI7dmFyIGs9bmV3IEJpZ0ludGVnZXIobywxMCk7dmFyIGE9ay50b1N0cmluZygyKTt2YXIgbD03LWEubGVuZ3RoJTc7aWYobD09Nyl7bD0wfXZhciBxPVwiXCI7Zm9yKHZhciBtPTA7bTxsO20rKyl7cSs9XCIwXCJ9YT1xK2E7Zm9yKHZhciBtPTA7bTxhLmxlbmd0aC0xO20rPTcpe3ZhciBwPWEuc3Vic3RyKG0sNyk7aWYobSE9YS5sZW5ndGgtNyl7cD1cIjFcIitwfW4rPWUocGFyc2VJbnQocCwyKSl9cmV0dXJuIG59O2lmKCFmLm1hdGNoKC9eWzAtOS5dKyQvKSl7dGhyb3dcIm1hbGZvcm1lZCBvaWQgc3RyaW5nOiBcIitmfXZhciBnPVwiXCI7dmFyIGI9Zi5zcGxpdChcIi5cIik7dmFyIGo9cGFyc2VJbnQoYlswXSkqNDArcGFyc2VJbnQoYlsxXSk7Zys9ZShqKTtiLnNwbGljZSgwLDIpO2Zvcih2YXIgYz0wO2M8Yi5sZW5ndGg7YysrKXtnKz1kKGJbY10pfXJldHVybiBnfTtLSlVSLmFzbjEuQVNOMU9iamVjdD1mdW5jdGlvbigpe3ZhciBjPXRydWU7dmFyIGI9bnVsbDt2YXIgZD1cIjAwXCI7dmFyIGU9XCIwMFwiO3ZhciBhPVwiXCI7dGhpcy5nZXRMZW5ndGhIZXhGcm9tVmFsdWU9ZnVuY3Rpb24oKXtpZih0eXBlb2YgdGhpcy5oVj09XCJ1bmRlZmluZWRcInx8dGhpcy5oVj09bnVsbCl7dGhyb3dcInRoaXMuaFYgaXMgbnVsbCBvciB1bmRlZmluZWQuXCJ9aWYodGhpcy5oVi5sZW5ndGglMj09MSl7dGhyb3dcInZhbHVlIGhleCBtdXN0IGJlIGV2ZW4gbGVuZ3RoOiBuPVwiK2EubGVuZ3RoK1wiLHY9XCIrdGhpcy5oVn12YXIgaT10aGlzLmhWLmxlbmd0aC8yO3ZhciBoPWkudG9TdHJpbmcoMTYpO2lmKGgubGVuZ3RoJTI9PTEpe2g9XCIwXCIraH1pZihpPDEyOCl7cmV0dXJuIGh9ZWxzZXt2YXIgZz1oLmxlbmd0aC8yO2lmKGc+MTUpe3Rocm93XCJBU04uMSBsZW5ndGggdG9vIGxvbmcgdG8gcmVwcmVzZW50IGJ5IDh4OiBuID0gXCIraS50b1N0cmluZygxNil9dmFyIGY9MTI4K2c7cmV0dXJuIGYudG9TdHJpbmcoMTYpK2h9fTt0aGlzLmdldEVuY29kZWRIZXg9ZnVuY3Rpb24oKXtpZih0aGlzLmhUTFY9PW51bGx8fHRoaXMuaXNNb2RpZmllZCl7dGhpcy5oVj10aGlzLmdldEZyZXNoVmFsdWVIZXgoKTt0aGlzLmhMPXRoaXMuZ2V0TGVuZ3RoSGV4RnJvbVZhbHVlKCk7dGhpcy5oVExWPXRoaXMuaFQrdGhpcy5oTCt0aGlzLmhWO3RoaXMuaXNNb2RpZmllZD1mYWxzZX1yZXR1cm4gdGhpcy5oVExWfTt0aGlzLmdldFZhbHVlSGV4PWZ1bmN0aW9uKCl7dGhpcy5nZXRFbmNvZGVkSGV4KCk7cmV0dXJuIHRoaXMuaFZ9O3RoaXMuZ2V0RnJlc2hWYWx1ZUhleD1mdW5jdGlvbigpe3JldHVyblwiXCJ9fTtLSlVSLmFzbjEuREVSQWJzdHJhY3RTdHJpbmc9ZnVuY3Rpb24oYyl7S0pVUi5hc24xLkRFUkFic3RyYWN0U3RyaW5nLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzKTt2YXIgYj1udWxsO3ZhciBhPW51bGw7dGhpcy5nZXRTdHJpbmc9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5zfTt0aGlzLnNldFN0cmluZz1mdW5jdGlvbihkKXt0aGlzLmhUTFY9bnVsbDt0aGlzLmlzTW9kaWZpZWQ9dHJ1ZTt0aGlzLnM9ZDt0aGlzLmhWPXV0Zjh0b2hleCh0aGlzLnMpLnRvTG93ZXJDYXNlKCl9O3RoaXMuc2V0U3RyaW5nSGV4PWZ1bmN0aW9uKGQpe3RoaXMuaFRMVj1udWxsO3RoaXMuaXNNb2RpZmllZD10cnVlO3RoaXMucz1udWxsO3RoaXMuaFY9ZH07dGhpcy5nZXRGcmVzaFZhbHVlSGV4PWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuaFZ9O2lmKHR5cGVvZiBjIT1cInVuZGVmaW5lZFwiKXtpZih0eXBlb2YgYz09XCJzdHJpbmdcIil7dGhpcy5zZXRTdHJpbmcoYyl9ZWxzZXtpZih0eXBlb2YgYy5zdHIhPVwidW5kZWZpbmVkXCIpe3RoaXMuc2V0U3RyaW5nKGMuc3RyKX1lbHNle2lmKHR5cGVvZiBjLmhleCE9XCJ1bmRlZmluZWRcIil7dGhpcy5zZXRTdHJpbmdIZXgoYy5oZXgpfX19fX07WUFIT08ubGFuZy5leHRlbmQoS0pVUi5hc24xLkRFUkFic3RyYWN0U3RyaW5nLEtKVVIuYXNuMS5BU04xT2JqZWN0KTtLSlVSLmFzbjEuREVSQWJzdHJhY3RUaW1lPWZ1bmN0aW9uKGMpe0tKVVIuYXNuMS5ERVJBYnN0cmFjdFRpbWUuc3VwZXJjbGFzcy5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMpO3ZhciBiPW51bGw7dmFyIGE9bnVsbDt0aGlzLmxvY2FsRGF0ZVRvVVRDPWZ1bmN0aW9uKGYpe3V0Yz1mLmdldFRpbWUoKSsoZi5nZXRUaW1lem9uZU9mZnNldCgpKjYwMDAwKTt2YXIgZT1uZXcgRGF0ZSh1dGMpO3JldHVybiBlfTt0aGlzLmZvcm1hdERhdGU9ZnVuY3Rpb24obSxvLGUpe3ZhciBnPXRoaXMuemVyb1BhZGRpbmc7dmFyIG49dGhpcy5sb2NhbERhdGVUb1VUQyhtKTt2YXIgcD1TdHJpbmcobi5nZXRGdWxsWWVhcigpKTtpZihvPT1cInV0Y1wiKXtwPXAuc3Vic3RyKDIsMil9dmFyIGw9ZyhTdHJpbmcobi5nZXRNb250aCgpKzEpLDIpO3ZhciBxPWcoU3RyaW5nKG4uZ2V0RGF0ZSgpKSwyKTt2YXIgaD1nKFN0cmluZyhuLmdldEhvdXJzKCkpLDIpO3ZhciBpPWcoU3RyaW5nKG4uZ2V0TWludXRlcygpKSwyKTt2YXIgaj1nKFN0cmluZyhuLmdldFNlY29uZHMoKSksMik7dmFyIHI9cCtsK3EraCtpK2o7aWYoZT09PXRydWUpe3ZhciBmPW4uZ2V0TWlsbGlzZWNvbmRzKCk7aWYoZiE9MCl7dmFyIGs9ZyhTdHJpbmcoZiksMyk7az1rLnJlcGxhY2UoL1swXSskLyxcIlwiKTtyPXIrXCIuXCIra319cmV0dXJuIHIrXCJaXCJ9O3RoaXMuemVyb1BhZGRpbmc9ZnVuY3Rpb24oZSxkKXtpZihlLmxlbmd0aD49ZCl7cmV0dXJuIGV9cmV0dXJuIG5ldyBBcnJheShkLWUubGVuZ3RoKzEpLmpvaW4oXCIwXCIpK2V9O3RoaXMuZ2V0U3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIHRoaXMuc307dGhpcy5zZXRTdHJpbmc9ZnVuY3Rpb24oZCl7dGhpcy5oVExWPW51bGw7dGhpcy5pc01vZGlmaWVkPXRydWU7dGhpcy5zPWQ7dGhpcy5oVj1zdG9oZXgoZCl9O3RoaXMuc2V0QnlEYXRlVmFsdWU9ZnVuY3Rpb24oaCxqLGUsZCxmLGcpe3ZhciBpPW5ldyBEYXRlKERhdGUuVVRDKGgsai0xLGUsZCxmLGcsMCkpO3RoaXMuc2V0QnlEYXRlKGkpfTt0aGlzLmdldEZyZXNoVmFsdWVIZXg9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5oVn19O1lBSE9PLmxhbmcuZXh0ZW5kKEtKVVIuYXNuMS5ERVJBYnN0cmFjdFRpbWUsS0pVUi5hc24xLkFTTjFPYmplY3QpO0tKVVIuYXNuMS5ERVJBYnN0cmFjdFN0cnVjdHVyZWQ9ZnVuY3Rpb24oYil7S0pVUi5hc24xLkRFUkFic3RyYWN0U3RyaW5nLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzKTt2YXIgYT1udWxsO3RoaXMuc2V0QnlBU04xT2JqZWN0QXJyYXk9ZnVuY3Rpb24oYyl7dGhpcy5oVExWPW51bGw7dGhpcy5pc01vZGlmaWVkPXRydWU7dGhpcy5hc24xQXJyYXk9Y307dGhpcy5hcHBlbmRBU04xT2JqZWN0PWZ1bmN0aW9uKGMpe3RoaXMuaFRMVj1udWxsO3RoaXMuaXNNb2RpZmllZD10cnVlO3RoaXMuYXNuMUFycmF5LnB1c2goYyl9O3RoaXMuYXNuMUFycmF5PW5ldyBBcnJheSgpO2lmKHR5cGVvZiBiIT1cInVuZGVmaW5lZFwiKXtpZih0eXBlb2YgYi5hcnJheSE9XCJ1bmRlZmluZWRcIil7dGhpcy5hc24xQXJyYXk9Yi5hcnJheX19fTtZQUhPTy5sYW5nLmV4dGVuZChLSlVSLmFzbjEuREVSQWJzdHJhY3RTdHJ1Y3R1cmVkLEtKVVIuYXNuMS5BU04xT2JqZWN0KTtLSlVSLmFzbjEuREVSQm9vbGVhbj1mdW5jdGlvbigpe0tKVVIuYXNuMS5ERVJCb29sZWFuLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzKTt0aGlzLmhUPVwiMDFcIjt0aGlzLmhUTFY9XCIwMTAxZmZcIn07WUFIT08ubGFuZy5leHRlbmQoS0pVUi5hc24xLkRFUkJvb2xlYW4sS0pVUi5hc24xLkFTTjFPYmplY3QpO0tKVVIuYXNuMS5ERVJJbnRlZ2VyPWZ1bmN0aW9uKGEpe0tKVVIuYXNuMS5ERVJJbnRlZ2VyLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzKTt0aGlzLmhUPVwiMDJcIjt0aGlzLnNldEJ5QmlnSW50ZWdlcj1mdW5jdGlvbihiKXt0aGlzLmhUTFY9bnVsbDt0aGlzLmlzTW9kaWZpZWQ9dHJ1ZTt0aGlzLmhWPUtKVVIuYXNuMS5BU04xVXRpbC5iaWdJbnRUb01pblR3b3NDb21wbGVtZW50c0hleChiKX07dGhpcy5zZXRCeUludGVnZXI9ZnVuY3Rpb24oYyl7dmFyIGI9bmV3IEJpZ0ludGVnZXIoU3RyaW5nKGMpLDEwKTt0aGlzLnNldEJ5QmlnSW50ZWdlcihiKX07dGhpcy5zZXRWYWx1ZUhleD1mdW5jdGlvbihiKXt0aGlzLmhWPWJ9O3RoaXMuZ2V0RnJlc2hWYWx1ZUhleD1mdW5jdGlvbigpe3JldHVybiB0aGlzLmhWfTtpZih0eXBlb2YgYSE9XCJ1bmRlZmluZWRcIil7aWYodHlwZW9mIGEuYmlnaW50IT1cInVuZGVmaW5lZFwiKXt0aGlzLnNldEJ5QmlnSW50ZWdlcihhLmJpZ2ludCl9ZWxzZXtpZih0eXBlb2YgYVtcImludFwiXSE9XCJ1bmRlZmluZWRcIil7dGhpcy5zZXRCeUludGVnZXIoYVtcImludFwiXSl9ZWxzZXtpZih0eXBlb2YgYT09XCJudW1iZXJcIil7dGhpcy5zZXRCeUludGVnZXIoYSl9ZWxzZXtpZih0eXBlb2YgYS5oZXghPVwidW5kZWZpbmVkXCIpe3RoaXMuc2V0VmFsdWVIZXgoYS5oZXgpfX19fX19O1lBSE9PLmxhbmcuZXh0ZW5kKEtKVVIuYXNuMS5ERVJJbnRlZ2VyLEtKVVIuYXNuMS5BU04xT2JqZWN0KTtLSlVSLmFzbjEuREVSQml0U3RyaW5nPWZ1bmN0aW9uKGIpe2lmKGIhPT11bmRlZmluZWQmJnR5cGVvZiBiLm9iaiE9PVwidW5kZWZpbmVkXCIpe3ZhciBhPUtKVVIuYXNuMS5BU04xVXRpbC5uZXdPYmplY3QoYi5vYmopO2IuaGV4PVwiMDBcIithLmdldEVuY29kZWRIZXgoKX1LSlVSLmFzbjEuREVSQml0U3RyaW5nLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzKTt0aGlzLmhUPVwiMDNcIjt0aGlzLnNldEhleFZhbHVlSW5jbHVkaW5nVW51c2VkQml0cz1mdW5jdGlvbihjKXt0aGlzLmhUTFY9bnVsbDt0aGlzLmlzTW9kaWZpZWQ9dHJ1ZTt0aGlzLmhWPWN9O3RoaXMuc2V0VW51c2VkQml0c0FuZEhleFZhbHVlPWZ1bmN0aW9uKGMsZSl7aWYoYzwwfHw3PGMpe3Rocm93XCJ1bnVzZWQgYml0cyBzaGFsbCBiZSBmcm9tIDAgdG8gNzogdSA9IFwiK2N9dmFyIGQ9XCIwXCIrYzt0aGlzLmhUTFY9bnVsbDt0aGlzLmlzTW9kaWZpZWQ9dHJ1ZTt0aGlzLmhWPWQrZX07dGhpcy5zZXRCeUJpbmFyeVN0cmluZz1mdW5jdGlvbihlKXtlPWUucmVwbGFjZSgvMCskLyxcIlwiKTt2YXIgZj04LWUubGVuZ3RoJTg7aWYoZj09OCl7Zj0wfWZvcih2YXIgZz0wO2c8PWY7ZysrKXtlKz1cIjBcIn12YXIgaj1cIlwiO2Zvcih2YXIgZz0wO2c8ZS5sZW5ndGgtMTtnKz04KXt2YXIgZD1lLnN1YnN0cihnLDgpO3ZhciBjPXBhcnNlSW50KGQsMikudG9TdHJpbmcoMTYpO2lmKGMubGVuZ3RoPT0xKXtjPVwiMFwiK2N9ais9Y310aGlzLmhUTFY9bnVsbDt0aGlzLmlzTW9kaWZpZWQ9dHJ1ZTt0aGlzLmhWPVwiMFwiK2Yran07dGhpcy5zZXRCeUJvb2xlYW5BcnJheT1mdW5jdGlvbihlKXt2YXIgZD1cIlwiO2Zvcih2YXIgYz0wO2M8ZS5sZW5ndGg7YysrKXtpZihlW2NdPT10cnVlKXtkKz1cIjFcIn1lbHNle2QrPVwiMFwifX10aGlzLnNldEJ5QmluYXJ5U3RyaW5nKGQpfTt0aGlzLm5ld0ZhbHNlQXJyYXk9ZnVuY3Rpb24oZSl7dmFyIGM9bmV3IEFycmF5KGUpO2Zvcih2YXIgZD0wO2Q8ZTtkKyspe2NbZF09ZmFsc2V9cmV0dXJuIGN9O3RoaXMuZ2V0RnJlc2hWYWx1ZUhleD1mdW5jdGlvbigpe3JldHVybiB0aGlzLmhWfTtpZih0eXBlb2YgYiE9XCJ1bmRlZmluZWRcIil7aWYodHlwZW9mIGI9PVwic3RyaW5nXCImJmIudG9Mb3dlckNhc2UoKS5tYXRjaCgvXlswLTlhLWZdKyQvKSl7dGhpcy5zZXRIZXhWYWx1ZUluY2x1ZGluZ1VudXNlZEJpdHMoYil9ZWxzZXtpZih0eXBlb2YgYi5oZXghPVwidW5kZWZpbmVkXCIpe3RoaXMuc2V0SGV4VmFsdWVJbmNsdWRpbmdVbnVzZWRCaXRzKGIuaGV4KX1lbHNle2lmKHR5cGVvZiBiLmJpbiE9XCJ1bmRlZmluZWRcIil7dGhpcy5zZXRCeUJpbmFyeVN0cmluZyhiLmJpbil9ZWxzZXtpZih0eXBlb2YgYi5hcnJheSE9XCJ1bmRlZmluZWRcIil7dGhpcy5zZXRCeUJvb2xlYW5BcnJheShiLmFycmF5KX19fX19fTtZQUhPTy5sYW5nLmV4dGVuZChLSlVSLmFzbjEuREVSQml0U3RyaW5nLEtKVVIuYXNuMS5BU04xT2JqZWN0KTtLSlVSLmFzbjEuREVST2N0ZXRTdHJpbmc9ZnVuY3Rpb24oYil7aWYoYiE9PXVuZGVmaW5lZCYmdHlwZW9mIGIub2JqIT09XCJ1bmRlZmluZWRcIil7dmFyIGE9S0pVUi5hc24xLkFTTjFVdGlsLm5ld09iamVjdChiLm9iaik7Yi5oZXg9YS5nZXRFbmNvZGVkSGV4KCl9S0pVUi5hc24xLkRFUk9jdGV0U3RyaW5nLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzLGIpO3RoaXMuaFQ9XCIwNFwifTtZQUhPTy5sYW5nLmV4dGVuZChLSlVSLmFzbjEuREVST2N0ZXRTdHJpbmcsS0pVUi5hc24xLkRFUkFic3RyYWN0U3RyaW5nKTtLSlVSLmFzbjEuREVSTnVsbD1mdW5jdGlvbigpe0tKVVIuYXNuMS5ERVJOdWxsLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzKTt0aGlzLmhUPVwiMDVcIjt0aGlzLmhUTFY9XCIwNTAwXCJ9O1lBSE9PLmxhbmcuZXh0ZW5kKEtKVVIuYXNuMS5ERVJOdWxsLEtKVVIuYXNuMS5BU04xT2JqZWN0KTtLSlVSLmFzbjEuREVST2JqZWN0SWRlbnRpZmllcj1mdW5jdGlvbihjKXt2YXIgYj1mdW5jdGlvbihkKXt2YXIgZT1kLnRvU3RyaW5nKDE2KTtpZihlLmxlbmd0aD09MSl7ZT1cIjBcIitlfXJldHVybiBlfTt2YXIgYT1mdW5jdGlvbihrKXt2YXIgaj1cIlwiO3ZhciBlPW5ldyBCaWdJbnRlZ2VyKGssMTApO3ZhciBkPWUudG9TdHJpbmcoMik7dmFyIGY9Ny1kLmxlbmd0aCU3O2lmKGY9PTcpe2Y9MH12YXIgbT1cIlwiO2Zvcih2YXIgZz0wO2c8ZjtnKyspe20rPVwiMFwifWQ9bStkO2Zvcih2YXIgZz0wO2c8ZC5sZW5ndGgtMTtnKz03KXt2YXIgbD1kLnN1YnN0cihnLDcpO2lmKGchPWQubGVuZ3RoLTcpe2w9XCIxXCIrbH1qKz1iKHBhcnNlSW50KGwsMikpfXJldHVybiBqfTtLSlVSLmFzbjEuREVST2JqZWN0SWRlbnRpZmllci5zdXBlcmNsYXNzLmNvbnN0cnVjdG9yLmNhbGwodGhpcyk7dGhpcy5oVD1cIjA2XCI7dGhpcy5zZXRWYWx1ZUhleD1mdW5jdGlvbihkKXt0aGlzLmhUTFY9bnVsbDt0aGlzLmlzTW9kaWZpZWQ9dHJ1ZTt0aGlzLnM9bnVsbDt0aGlzLmhWPWR9O3RoaXMuc2V0VmFsdWVPaWRTdHJpbmc9ZnVuY3Rpb24oZil7aWYoIWYubWF0Y2goL15bMC05Ll0rJC8pKXt0aHJvd1wibWFsZm9ybWVkIG9pZCBzdHJpbmc6IFwiK2Z9dmFyIGc9XCJcIjt2YXIgZD1mLnNwbGl0KFwiLlwiKTt2YXIgaj1wYXJzZUludChkWzBdKSo0MCtwYXJzZUludChkWzFdKTtnKz1iKGopO2Quc3BsaWNlKDAsMik7Zm9yKHZhciBlPTA7ZTxkLmxlbmd0aDtlKyspe2crPWEoZFtlXSl9dGhpcy5oVExWPW51bGw7dGhpcy5pc01vZGlmaWVkPXRydWU7dGhpcy5zPW51bGw7dGhpcy5oVj1nfTt0aGlzLnNldFZhbHVlTmFtZT1mdW5jdGlvbihlKXt2YXIgZD1LSlVSLmFzbjEueDUwOS5PSUQubmFtZTJvaWQoZSk7aWYoZCE9PVwiXCIpe3RoaXMuc2V0VmFsdWVPaWRTdHJpbmcoZCl9ZWxzZXt0aHJvd1wiREVST2JqZWN0SWRlbnRpZmllciBvaWROYW1lIHVuZGVmaW5lZDogXCIrZX19O3RoaXMuZ2V0RnJlc2hWYWx1ZUhleD1mdW5jdGlvbigpe3JldHVybiB0aGlzLmhWfTtpZihjIT09dW5kZWZpbmVkKXtpZih0eXBlb2YgYz09PVwic3RyaW5nXCIpe2lmKGMubWF0Y2goL15bMC0yXS5bMC05Ll0rJC8pKXt0aGlzLnNldFZhbHVlT2lkU3RyaW5nKGMpfWVsc2V7dGhpcy5zZXRWYWx1ZU5hbWUoYyl9fWVsc2V7aWYoYy5vaWQhPT11bmRlZmluZWQpe3RoaXMuc2V0VmFsdWVPaWRTdHJpbmcoYy5vaWQpfWVsc2V7aWYoYy5oZXghPT11bmRlZmluZWQpe3RoaXMuc2V0VmFsdWVIZXgoYy5oZXgpfWVsc2V7aWYoYy5uYW1lIT09dW5kZWZpbmVkKXt0aGlzLnNldFZhbHVlTmFtZShjLm5hbWUpfX19fX19O1lBSE9PLmxhbmcuZXh0ZW5kKEtKVVIuYXNuMS5ERVJPYmplY3RJZGVudGlmaWVyLEtKVVIuYXNuMS5BU04xT2JqZWN0KTtLSlVSLmFzbjEuREVSRW51bWVyYXRlZD1mdW5jdGlvbihhKXtLSlVSLmFzbjEuREVSRW51bWVyYXRlZC5zdXBlcmNsYXNzLmNvbnN0cnVjdG9yLmNhbGwodGhpcyk7dGhpcy5oVD1cIjBhXCI7dGhpcy5zZXRCeUJpZ0ludGVnZXI9ZnVuY3Rpb24oYil7dGhpcy5oVExWPW51bGw7dGhpcy5pc01vZGlmaWVkPXRydWU7dGhpcy5oVj1LSlVSLmFzbjEuQVNOMVV0aWwuYmlnSW50VG9NaW5Ud29zQ29tcGxlbWVudHNIZXgoYil9O3RoaXMuc2V0QnlJbnRlZ2VyPWZ1bmN0aW9uKGMpe3ZhciBiPW5ldyBCaWdJbnRlZ2VyKFN0cmluZyhjKSwxMCk7dGhpcy5zZXRCeUJpZ0ludGVnZXIoYil9O3RoaXMuc2V0VmFsdWVIZXg9ZnVuY3Rpb24oYil7dGhpcy5oVj1ifTt0aGlzLmdldEZyZXNoVmFsdWVIZXg9ZnVuY3Rpb24oKXtyZXR1cm4gdGhpcy5oVn07aWYodHlwZW9mIGEhPVwidW5kZWZpbmVkXCIpe2lmKHR5cGVvZiBhW1wiaW50XCJdIT1cInVuZGVmaW5lZFwiKXt0aGlzLnNldEJ5SW50ZWdlcihhW1wiaW50XCJdKX1lbHNle2lmKHR5cGVvZiBhPT1cIm51bWJlclwiKXt0aGlzLnNldEJ5SW50ZWdlcihhKX1lbHNle2lmKHR5cGVvZiBhLmhleCE9XCJ1bmRlZmluZWRcIil7dGhpcy5zZXRWYWx1ZUhleChhLmhleCl9fX19fTtZQUhPTy5sYW5nLmV4dGVuZChLSlVSLmFzbjEuREVSRW51bWVyYXRlZCxLSlVSLmFzbjEuQVNOMU9iamVjdCk7S0pVUi5hc24xLkRFUlVURjhTdHJpbmc9ZnVuY3Rpb24oYSl7S0pVUi5hc24xLkRFUlVURjhTdHJpbmcuc3VwZXJjbGFzcy5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMsYSk7dGhpcy5oVD1cIjBjXCJ9O1lBSE9PLmxhbmcuZXh0ZW5kKEtKVVIuYXNuMS5ERVJVVEY4U3RyaW5nLEtKVVIuYXNuMS5ERVJBYnN0cmFjdFN0cmluZyk7S0pVUi5hc24xLkRFUk51bWVyaWNTdHJpbmc9ZnVuY3Rpb24oYSl7S0pVUi5hc24xLkRFUk51bWVyaWNTdHJpbmcuc3VwZXJjbGFzcy5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMsYSk7dGhpcy5oVD1cIjEyXCJ9O1lBSE9PLmxhbmcuZXh0ZW5kKEtKVVIuYXNuMS5ERVJOdW1lcmljU3RyaW5nLEtKVVIuYXNuMS5ERVJBYnN0cmFjdFN0cmluZyk7S0pVUi5hc24xLkRFUlByaW50YWJsZVN0cmluZz1mdW5jdGlvbihhKXtLSlVSLmFzbjEuREVSUHJpbnRhYmxlU3RyaW5nLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzLGEpO3RoaXMuaFQ9XCIxM1wifTtZQUhPTy5sYW5nLmV4dGVuZChLSlVSLmFzbjEuREVSUHJpbnRhYmxlU3RyaW5nLEtKVVIuYXNuMS5ERVJBYnN0cmFjdFN0cmluZyk7S0pVUi5hc24xLkRFUlRlbGV0ZXhTdHJpbmc9ZnVuY3Rpb24oYSl7S0pVUi5hc24xLkRFUlRlbGV0ZXhTdHJpbmcuc3VwZXJjbGFzcy5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMsYSk7dGhpcy5oVD1cIjE0XCJ9O1lBSE9PLmxhbmcuZXh0ZW5kKEtKVVIuYXNuMS5ERVJUZWxldGV4U3RyaW5nLEtKVVIuYXNuMS5ERVJBYnN0cmFjdFN0cmluZyk7S0pVUi5hc24xLkRFUklBNVN0cmluZz1mdW5jdGlvbihhKXtLSlVSLmFzbjEuREVSSUE1U3RyaW5nLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzLGEpO3RoaXMuaFQ9XCIxNlwifTtZQUhPTy5sYW5nLmV4dGVuZChLSlVSLmFzbjEuREVSSUE1U3RyaW5nLEtKVVIuYXNuMS5ERVJBYnN0cmFjdFN0cmluZyk7S0pVUi5hc24xLkRFUlVUQ1RpbWU9ZnVuY3Rpb24oYSl7S0pVUi5hc24xLkRFUlVUQ1RpbWUuc3VwZXJjbGFzcy5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMsYSk7dGhpcy5oVD1cIjE3XCI7dGhpcy5zZXRCeURhdGU9ZnVuY3Rpb24oYil7dGhpcy5oVExWPW51bGw7dGhpcy5pc01vZGlmaWVkPXRydWU7dGhpcy5kYXRlPWI7dGhpcy5zPXRoaXMuZm9ybWF0RGF0ZSh0aGlzLmRhdGUsXCJ1dGNcIik7dGhpcy5oVj1zdG9oZXgodGhpcy5zKX07dGhpcy5nZXRGcmVzaFZhbHVlSGV4PWZ1bmN0aW9uKCl7aWYodHlwZW9mIHRoaXMuZGF0ZT09XCJ1bmRlZmluZWRcIiYmdHlwZW9mIHRoaXMucz09XCJ1bmRlZmluZWRcIil7dGhpcy5kYXRlPW5ldyBEYXRlKCk7dGhpcy5zPXRoaXMuZm9ybWF0RGF0ZSh0aGlzLmRhdGUsXCJ1dGNcIik7dGhpcy5oVj1zdG9oZXgodGhpcy5zKX1yZXR1cm4gdGhpcy5oVn07aWYoYSE9PXVuZGVmaW5lZCl7aWYoYS5zdHIhPT11bmRlZmluZWQpe3RoaXMuc2V0U3RyaW5nKGEuc3RyKX1lbHNle2lmKHR5cGVvZiBhPT1cInN0cmluZ1wiJiZhLm1hdGNoKC9eWzAtOV17MTJ9WiQvKSl7dGhpcy5zZXRTdHJpbmcoYSl9ZWxzZXtpZihhLmhleCE9PXVuZGVmaW5lZCl7dGhpcy5zZXRTdHJpbmdIZXgoYS5oZXgpfWVsc2V7aWYoYS5kYXRlIT09dW5kZWZpbmVkKXt0aGlzLnNldEJ5RGF0ZShhLmRhdGUpfX19fX19O1lBSE9PLmxhbmcuZXh0ZW5kKEtKVVIuYXNuMS5ERVJVVENUaW1lLEtKVVIuYXNuMS5ERVJBYnN0cmFjdFRpbWUpO0tKVVIuYXNuMS5ERVJHZW5lcmFsaXplZFRpbWU9ZnVuY3Rpb24oYSl7S0pVUi5hc24xLkRFUkdlbmVyYWxpemVkVGltZS5zdXBlcmNsYXNzLmNvbnN0cnVjdG9yLmNhbGwodGhpcyxhKTt0aGlzLmhUPVwiMThcIjt0aGlzLndpdGhNaWxsaXM9ZmFsc2U7dGhpcy5zZXRCeURhdGU9ZnVuY3Rpb24oYil7dGhpcy5oVExWPW51bGw7dGhpcy5pc01vZGlmaWVkPXRydWU7dGhpcy5kYXRlPWI7dGhpcy5zPXRoaXMuZm9ybWF0RGF0ZSh0aGlzLmRhdGUsXCJnZW5cIix0aGlzLndpdGhNaWxsaXMpO3RoaXMuaFY9c3RvaGV4KHRoaXMucyl9O3RoaXMuZ2V0RnJlc2hWYWx1ZUhleD1mdW5jdGlvbigpe2lmKHRoaXMuZGF0ZT09PXVuZGVmaW5lZCYmdGhpcy5zPT09dW5kZWZpbmVkKXt0aGlzLmRhdGU9bmV3IERhdGUoKTt0aGlzLnM9dGhpcy5mb3JtYXREYXRlKHRoaXMuZGF0ZSxcImdlblwiLHRoaXMud2l0aE1pbGxpcyk7dGhpcy5oVj1zdG9oZXgodGhpcy5zKX1yZXR1cm4gdGhpcy5oVn07aWYoYSE9PXVuZGVmaW5lZCl7aWYoYS5zdHIhPT11bmRlZmluZWQpe3RoaXMuc2V0U3RyaW5nKGEuc3RyKX1lbHNle2lmKHR5cGVvZiBhPT1cInN0cmluZ1wiJiZhLm1hdGNoKC9eWzAtOV17MTR9WiQvKSl7dGhpcy5zZXRTdHJpbmcoYSl9ZWxzZXtpZihhLmhleCE9PXVuZGVmaW5lZCl7dGhpcy5zZXRTdHJpbmdIZXgoYS5oZXgpfWVsc2V7aWYoYS5kYXRlIT09dW5kZWZpbmVkKXt0aGlzLnNldEJ5RGF0ZShhLmRhdGUpfX19fWlmKGEubWlsbGlzPT09dHJ1ZSl7dGhpcy53aXRoTWlsbGlzPXRydWV9fX07WUFIT08ubGFuZy5leHRlbmQoS0pVUi5hc24xLkRFUkdlbmVyYWxpemVkVGltZSxLSlVSLmFzbjEuREVSQWJzdHJhY3RUaW1lKTtLSlVSLmFzbjEuREVSU2VxdWVuY2U9ZnVuY3Rpb24oYSl7S0pVUi5hc24xLkRFUlNlcXVlbmNlLnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzLGEpO3RoaXMuaFQ9XCIzMFwiO3RoaXMuZ2V0RnJlc2hWYWx1ZUhleD1mdW5jdGlvbigpe3ZhciBjPVwiXCI7Zm9yKHZhciBiPTA7Yjx0aGlzLmFzbjFBcnJheS5sZW5ndGg7YisrKXt2YXIgZD10aGlzLmFzbjFBcnJheVtiXTtjKz1kLmdldEVuY29kZWRIZXgoKX10aGlzLmhWPWM7cmV0dXJuIHRoaXMuaFZ9fTtZQUhPTy5sYW5nLmV4dGVuZChLSlVSLmFzbjEuREVSU2VxdWVuY2UsS0pVUi5hc24xLkRFUkFic3RyYWN0U3RydWN0dXJlZCk7S0pVUi5hc24xLkRFUlNldD1mdW5jdGlvbihhKXtLSlVSLmFzbjEuREVSU2V0LnN1cGVyY2xhc3MuY29uc3RydWN0b3IuY2FsbCh0aGlzLGEpO3RoaXMuaFQ9XCIzMVwiO3RoaXMuc29ydEZsYWc9dHJ1ZTt0aGlzLmdldEZyZXNoVmFsdWVIZXg9ZnVuY3Rpb24oKXt2YXIgYj1uZXcgQXJyYXkoKTtmb3IodmFyIGM9MDtjPHRoaXMuYXNuMUFycmF5Lmxlbmd0aDtjKyspe3ZhciBkPXRoaXMuYXNuMUFycmF5W2NdO2IucHVzaChkLmdldEVuY29kZWRIZXgoKSl9aWYodGhpcy5zb3J0RmxhZz09dHJ1ZSl7Yi5zb3J0KCl9dGhpcy5oVj1iLmpvaW4oXCJcIik7cmV0dXJuIHRoaXMuaFZ9O2lmKHR5cGVvZiBhIT1cInVuZGVmaW5lZFwiKXtpZih0eXBlb2YgYS5zb3J0ZmxhZyE9XCJ1bmRlZmluZWRcIiYmYS5zb3J0ZmxhZz09ZmFsc2Upe3RoaXMuc29ydEZsYWc9ZmFsc2V9fX07WUFIT08ubGFuZy5leHRlbmQoS0pVUi5hc24xLkRFUlNldCxLSlVSLmFzbjEuREVSQWJzdHJhY3RTdHJ1Y3R1cmVkKTtLSlVSLmFzbjEuREVSVGFnZ2VkT2JqZWN0PWZ1bmN0aW9uKGEpe0tKVVIuYXNuMS5ERVJUYWdnZWRPYmplY3Quc3VwZXJjbGFzcy5jb25zdHJ1Y3Rvci5jYWxsKHRoaXMpO3RoaXMuaFQ9XCJhMFwiO3RoaXMuaFY9XCJcIjt0aGlzLmlzRXhwbGljaXQ9dHJ1ZTt0aGlzLmFzbjFPYmplY3Q9bnVsbDt0aGlzLnNldEFTTjFPYmplY3Q9ZnVuY3Rpb24oYixjLGQpe3RoaXMuaFQ9Yzt0aGlzLmlzRXhwbGljaXQ9Yjt0aGlzLmFzbjFPYmplY3Q9ZDtpZih0aGlzLmlzRXhwbGljaXQpe3RoaXMuaFY9dGhpcy5hc24xT2JqZWN0LmdldEVuY29kZWRIZXgoKTt0aGlzLmhUTFY9bnVsbDt0aGlzLmlzTW9kaWZpZWQ9dHJ1ZX1lbHNle3RoaXMuaFY9bnVsbDt0aGlzLmhUTFY9ZC5nZXRFbmNvZGVkSGV4KCk7dGhpcy5oVExWPXRoaXMuaFRMVi5yZXBsYWNlKC9eLi4vLGMpO3RoaXMuaXNNb2RpZmllZD1mYWxzZX19O3RoaXMuZ2V0RnJlc2hWYWx1ZUhleD1mdW5jdGlvbigpe3JldHVybiB0aGlzLmhWfTtpZih0eXBlb2YgYSE9XCJ1bmRlZmluZWRcIil7aWYodHlwZW9mIGEudGFnIT1cInVuZGVmaW5lZFwiKXt0aGlzLmhUPWEudGFnfWlmKHR5cGVvZiBhLmV4cGxpY2l0IT1cInVuZGVmaW5lZFwiKXt0aGlzLmlzRXhwbGljaXQ9YS5leHBsaWNpdH1pZih0eXBlb2YgYS5vYmohPVwidW5kZWZpbmVkXCIpe3RoaXMuYXNuMU9iamVjdD1hLm9iajt0aGlzLnNldEFTTjFPYmplY3QodGhpcy5pc0V4cGxpY2l0LHRoaXMuaFQsdGhpcy5hc24xT2JqZWN0KX19fTtZQUhPTy5sYW5nLmV4dGVuZChLSlVSLmFzbjEuREVSVGFnZ2VkT2JqZWN0LEtKVVIuYXNuMS5BU04xT2JqZWN0KTtcbnZhciBBU04xSEVYPW5ldyBmdW5jdGlvbigpe307QVNOMUhFWC5nZXRMYmxlbj1mdW5jdGlvbihjLGEpe2lmKGMuc3Vic3RyKGErMiwxKSE9XCI4XCIpe3JldHVybiAxfXZhciBiPXBhcnNlSW50KGMuc3Vic3RyKGErMywxKSk7aWYoYj09MCl7cmV0dXJuIC0xfWlmKDA8YiYmYjwxMCl7cmV0dXJuIGIrMX1yZXR1cm4gLTJ9O0FTTjFIRVguZ2V0TD1mdW5jdGlvbihjLGIpe3ZhciBhPUFTTjFIRVguZ2V0TGJsZW4oYyxiKTtpZihhPDEpe3JldHVyblwiXCJ9cmV0dXJuIGMuc3Vic3RyKGIrMixhKjIpfTtBU04xSEVYLmdldFZibGVuPWZ1bmN0aW9uKGQsYSl7dmFyIGMsYjtjPUFTTjFIRVguZ2V0TChkLGEpO2lmKGM9PVwiXCIpe3JldHVybiAtMX1pZihjLnN1YnN0cigwLDEpPT09XCI4XCIpe2I9bmV3IEJpZ0ludGVnZXIoYy5zdWJzdHIoMiksMTYpfWVsc2V7Yj1uZXcgQmlnSW50ZWdlcihjLDE2KX1yZXR1cm4gYi5pbnRWYWx1ZSgpfTtBU04xSEVYLmdldFZpZHg9ZnVuY3Rpb24oYyxiKXt2YXIgYT1BU04xSEVYLmdldExibGVuKGMsYik7aWYoYTwwKXtyZXR1cm4gYX1yZXR1cm4gYisoYSsxKSoyfTtBU04xSEVYLmdldFY9ZnVuY3Rpb24oZCxhKXt2YXIgYz1BU04xSEVYLmdldFZpZHgoZCxhKTt2YXIgYj1BU04xSEVYLmdldFZibGVuKGQsYSk7cmV0dXJuIGQuc3Vic3RyKGMsYioyKX07QVNOMUhFWC5nZXRUTFY9ZnVuY3Rpb24oYixhKXtyZXR1cm4gYi5zdWJzdHIoYSwyKStBU04xSEVYLmdldEwoYixhKStBU04xSEVYLmdldFYoYixhKX07QVNOMUhFWC5nZXROZXh0U2libGluZ0lkeD1mdW5jdGlvbihkLGEpe3ZhciBjPUFTTjFIRVguZ2V0VmlkeChkLGEpO3ZhciBiPUFTTjFIRVguZ2V0VmJsZW4oZCxhKTtyZXR1cm4gYytiKjJ9O0FTTjFIRVguZ2V0Q2hpbGRJZHg9ZnVuY3Rpb24oZSxmKXt2YXIgaj1BU04xSEVYO3ZhciBnPW5ldyBBcnJheSgpO3ZhciBpPWouZ2V0VmlkeChlLGYpO2lmKGUuc3Vic3RyKGYsMik9PVwiMDNcIil7Zy5wdXNoKGkrMil9ZWxzZXtnLnB1c2goaSl9dmFyIGw9ai5nZXRWYmxlbihlLGYpO3ZhciBjPWk7dmFyIGQ9MDt3aGlsZSgxKXt2YXIgYj1qLmdldE5leHRTaWJsaW5nSWR4KGUsYyk7aWYoYj09bnVsbHx8KGItaT49KGwqMikpKXticmVha31pZihkPj0yMDApe2JyZWFrfWcucHVzaChiKTtjPWI7ZCsrfXJldHVybiBnfTtBU04xSEVYLmdldE50aENoaWxkSWR4PWZ1bmN0aW9uKGQsYixlKXt2YXIgYz1BU04xSEVYLmdldENoaWxkSWR4KGQsYik7cmV0dXJuIGNbZV19O0FTTjFIRVguZ2V0SWR4YnlMaXN0PWZ1bmN0aW9uKGUsZCxjLGkpe3ZhciBnPUFTTjFIRVg7dmFyIGYsYjtpZihjLmxlbmd0aD09MCl7aWYoaSE9PXVuZGVmaW5lZCl7aWYoZS5zdWJzdHIoZCwyKSE9PWkpe3Rocm93XCJjaGVja2luZyB0YWcgZG9lc24ndCBtYXRjaDogXCIrZS5zdWJzdHIoZCwyKStcIiE9XCIraX19cmV0dXJuIGR9Zj1jLnNoaWZ0KCk7Yj1nLmdldENoaWxkSWR4KGUsZCk7cmV0dXJuIGcuZ2V0SWR4YnlMaXN0KGUsYltmXSxjLGkpfTtBU04xSEVYLmdldFRMVmJ5TGlzdD1mdW5jdGlvbihkLGMsYixmKXt2YXIgZT1BU04xSEVYO3ZhciBhPWUuZ2V0SWR4YnlMaXN0KGQsYyxiKTtpZihhPT09dW5kZWZpbmVkKXt0aHJvd1wiY2FuJ3QgZmluZCBudGhMaXN0IG9iamVjdFwifWlmKGYhPT11bmRlZmluZWQpe2lmKGQuc3Vic3RyKGEsMikhPWYpe3Rocm93XCJjaGVja2luZyB0YWcgZG9lc24ndCBtYXRjaDogXCIrZC5zdWJzdHIoYSwyKStcIiE9XCIrZn19cmV0dXJuIGUuZ2V0VExWKGQsYSl9O0FTTjFIRVguZ2V0VmJ5TGlzdD1mdW5jdGlvbihlLGMsYixnLGkpe3ZhciBmPUFTTjFIRVg7dmFyIGEsZDthPWYuZ2V0SWR4YnlMaXN0KGUsYyxiLGcpO2lmKGE9PT11bmRlZmluZWQpe3Rocm93XCJjYW4ndCBmaW5kIG50aExpc3Qgb2JqZWN0XCJ9ZD1mLmdldFYoZSxhKTtpZihpPT09dHJ1ZSl7ZD1kLnN1YnN0cigyKX1yZXR1cm4gZH07QVNOMUhFWC5oZXh0b29pZHN0cj1mdW5jdGlvbihlKXt2YXIgaD1mdW5jdGlvbihiLGEpe2lmKGIubGVuZ3RoPj1hKXtyZXR1cm4gYn1yZXR1cm4gbmV3IEFycmF5KGEtYi5sZW5ndGgrMSkuam9pbihcIjBcIikrYn07dmFyIGw9W107dmFyIG89ZS5zdWJzdHIoMCwyKTt2YXIgZj1wYXJzZUludChvLDE2KTtsWzBdPW5ldyBTdHJpbmcoTWF0aC5mbG9vcihmLzQwKSk7bFsxXT1uZXcgU3RyaW5nKGYlNDApO3ZhciBtPWUuc3Vic3RyKDIpO3ZhciBrPVtdO2Zvcih2YXIgZz0wO2c8bS5sZW5ndGgvMjtnKyspe2sucHVzaChwYXJzZUludChtLnN1YnN0cihnKjIsMiksMTYpKX12YXIgaj1bXTt2YXIgZD1cIlwiO2Zvcih2YXIgZz0wO2c8ay5sZW5ndGg7ZysrKXtpZihrW2ddJjEyOCl7ZD1kK2goKGtbZ10mMTI3KS50b1N0cmluZygyKSw3KX1lbHNle2Q9ZCtoKChrW2ddJjEyNykudG9TdHJpbmcoMiksNyk7ai5wdXNoKG5ldyBTdHJpbmcocGFyc2VJbnQoZCwyKSkpO2Q9XCJcIn19dmFyIG49bC5qb2luKFwiLlwiKTtpZihqLmxlbmd0aD4wKXtuPW4rXCIuXCIrai5qb2luKFwiLlwiKX1yZXR1cm4gbn07QVNOMUhFWC5kdW1wPWZ1bmN0aW9uKHQsYyxsLGcpe3ZhciBwPUFTTjFIRVg7dmFyIGo9cC5nZXRWO3ZhciB5PXAuZHVtcDt2YXIgdz1wLmdldENoaWxkSWR4O3ZhciBlPXQ7aWYodCBpbnN0YW5jZW9mIEtKVVIuYXNuMS5BU04xT2JqZWN0KXtlPXQuZ2V0RW5jb2RlZEhleCgpfXZhciBxPWZ1bmN0aW9uKEEsaSl7aWYoQS5sZW5ndGg8PWkqMil7cmV0dXJuIEF9ZWxzZXt2YXIgdj1BLnN1YnN0cigwLGkpK1wiLi4odG90YWwgXCIrQS5sZW5ndGgvMitcImJ5dGVzKS4uXCIrQS5zdWJzdHIoQS5sZW5ndGgtaSxpKTtyZXR1cm4gdn19O2lmKGM9PT11bmRlZmluZWQpe2M9e29tbWl0X2xvbmdfb2N0ZXQ6MzJ9fWlmKGw9PT11bmRlZmluZWQpe2w9MH1pZihnPT09dW5kZWZpbmVkKXtnPVwiXCJ9dmFyIHg9Yy5vbW1pdF9sb25nX29jdGV0O2lmKGUuc3Vic3RyKGwsMik9PVwiMDFcIil7dmFyIGg9aihlLGwpO2lmKGg9PVwiMDBcIil7cmV0dXJuIGcrXCJCT09MRUFOIEZBTFNFXFxuXCJ9ZWxzZXtyZXR1cm4gZytcIkJPT0xFQU4gVFJVRVxcblwifX1pZihlLnN1YnN0cihsLDIpPT1cIjAyXCIpe3ZhciBoPWooZSxsKTtyZXR1cm4gZytcIklOVEVHRVIgXCIrcShoLHgpK1wiXFxuXCJ9aWYoZS5zdWJzdHIobCwyKT09XCIwM1wiKXt2YXIgaD1qKGUsbCk7cmV0dXJuIGcrXCJCSVRTVFJJTkcgXCIrcShoLHgpK1wiXFxuXCJ9aWYoZS5zdWJzdHIobCwyKT09XCIwNFwiKXt2YXIgaD1qKGUsbCk7aWYocC5pc0FTTjFIRVgoaCkpe3ZhciBrPWcrXCJPQ1RFVFNUUklORywgZW5jYXBzdWxhdGVzXFxuXCI7az1rK3koaCxjLDAsZytcIiAgXCIpO3JldHVybiBrfWVsc2V7cmV0dXJuIGcrXCJPQ1RFVFNUUklORyBcIitxKGgseCkrXCJcXG5cIn19aWYoZS5zdWJzdHIobCwyKT09XCIwNVwiKXtyZXR1cm4gZytcIk5VTExcXG5cIn1pZihlLnN1YnN0cihsLDIpPT1cIjA2XCIpe3ZhciBtPWooZSxsKTt2YXIgYT1LSlVSLmFzbjEuQVNOMVV0aWwub2lkSGV4VG9JbnQobSk7dmFyIG89S0pVUi5hc24xLng1MDkuT0lELm9pZDJuYW1lKGEpO3ZhciBiPWEucmVwbGFjZSgvXFwuL2csXCIgXCIpO2lmKG8hPVwiXCIpe3JldHVybiBnK1wiT2JqZWN0SWRlbnRpZmllciBcIitvK1wiIChcIitiK1wiKVxcblwifWVsc2V7cmV0dXJuIGcrXCJPYmplY3RJZGVudGlmaWVyIChcIitiK1wiKVxcblwifX1pZihlLnN1YnN0cihsLDIpPT1cIjBjXCIpe3JldHVybiBnK1wiVVRGOFN0cmluZyAnXCIraGV4dG91dGY4KGooZSxsKSkrXCInXFxuXCJ9aWYoZS5zdWJzdHIobCwyKT09XCIxM1wiKXtyZXR1cm4gZytcIlByaW50YWJsZVN0cmluZyAnXCIraGV4dG91dGY4KGooZSxsKSkrXCInXFxuXCJ9aWYoZS5zdWJzdHIobCwyKT09XCIxNFwiKXtyZXR1cm4gZytcIlRlbGV0ZXhTdHJpbmcgJ1wiK2hleHRvdXRmOChqKGUsbCkpK1wiJ1xcblwifWlmKGUuc3Vic3RyKGwsMik9PVwiMTZcIil7cmV0dXJuIGcrXCJJQTVTdHJpbmcgJ1wiK2hleHRvdXRmOChqKGUsbCkpK1wiJ1xcblwifWlmKGUuc3Vic3RyKGwsMik9PVwiMTdcIil7cmV0dXJuIGcrXCJVVENUaW1lIFwiK2hleHRvdXRmOChqKGUsbCkpK1wiXFxuXCJ9aWYoZS5zdWJzdHIobCwyKT09XCIxOFwiKXtyZXR1cm4gZytcIkdlbmVyYWxpemVkVGltZSBcIitoZXh0b3V0ZjgoaihlLGwpKStcIlxcblwifWlmKGUuc3Vic3RyKGwsMik9PVwiMzBcIil7aWYoZS5zdWJzdHIobCw0KT09XCIzMDAwXCIpe3JldHVybiBnK1wiU0VRVUVOQ0Uge31cXG5cIn12YXIgaz1nK1wiU0VRVUVOQ0VcXG5cIjt2YXIgZD13KGUsbCk7dmFyIGY9YztpZigoZC5sZW5ndGg9PTJ8fGQubGVuZ3RoPT0zKSYmZS5zdWJzdHIoZFswXSwyKT09XCIwNlwiJiZlLnN1YnN0cihkW2QubGVuZ3RoLTFdLDIpPT1cIjA0XCIpe3ZhciBvPXAub2lkbmFtZShqKGUsZFswXSkpO3ZhciByPUpTT04ucGFyc2UoSlNPTi5zdHJpbmdpZnkoYykpO3IueDUwOUV4dE5hbWU9bztmPXJ9Zm9yKHZhciB1PTA7dTxkLmxlbmd0aDt1Kyspe2s9ayt5KGUsZixkW3VdLGcrXCIgIFwiKX1yZXR1cm4ga31pZihlLnN1YnN0cihsLDIpPT1cIjMxXCIpe3ZhciBrPWcrXCJTRVRcXG5cIjt2YXIgZD13KGUsbCk7Zm9yKHZhciB1PTA7dTxkLmxlbmd0aDt1Kyspe2s9ayt5KGUsYyxkW3VdLGcrXCIgIFwiKX1yZXR1cm4ga312YXIgej1wYXJzZUludChlLnN1YnN0cihsLDIpLDE2KTtpZigoeiYxMjgpIT0wKXt2YXIgbj16JjMxO2lmKCh6JjMyKSE9MCl7dmFyIGs9ZytcIltcIituK1wiXVxcblwiO3ZhciBkPXcoZSxsKTtmb3IodmFyIHU9MDt1PGQubGVuZ3RoO3UrKyl7az1rK3koZSxjLGRbdV0sZytcIiAgXCIpfXJldHVybiBrfWVsc2V7dmFyIGg9aihlLGwpO2lmKGguc3Vic3RyKDAsOCk9PVwiNjg3NDc0NzBcIil7aD1oZXh0b3V0ZjgoaCl9aWYoYy54NTA5RXh0TmFtZT09PVwic3ViamVjdEFsdE5hbWVcIiYmbj09Mil7aD1oZXh0b3V0ZjgoaCl9dmFyIGs9ZytcIltcIituK1wiXSBcIitoK1wiXFxuXCI7cmV0dXJuIGt9fXJldHVybiBnK1wiVU5LTk9XTihcIitlLnN1YnN0cihsLDIpK1wiKSBcIitqKGUsbCkrXCJcXG5cIn07QVNOMUhFWC5pc0FTTjFIRVg9ZnVuY3Rpb24oZSl7dmFyIGQ9QVNOMUhFWDtpZihlLmxlbmd0aCUyPT0xKXtyZXR1cm4gZmFsc2V9dmFyIGM9ZC5nZXRWYmxlbihlLDApO3ZhciBiPWUuc3Vic3RyKDAsMik7dmFyIGY9ZC5nZXRMKGUsMCk7dmFyIGE9ZS5sZW5ndGgtYi5sZW5ndGgtZi5sZW5ndGg7aWYoYT09YyoyKXtyZXR1cm4gdHJ1ZX1yZXR1cm4gZmFsc2V9O0FTTjFIRVgub2lkbmFtZT1mdW5jdGlvbihhKXt2YXIgYz1LSlVSLmFzbjE7aWYoS0pVUi5sYW5nLlN0cmluZy5pc0hleChhKSl7YT1jLkFTTjFVdGlsLm9pZEhleFRvSW50KGEpfXZhciBiPWMueDUwOS5PSUQub2lkMm5hbWUoYSk7aWYoYj09PVwiXCIpe2I9YX1yZXR1cm4gYn07XG52YXIgS0pVUjtpZih0eXBlb2YgS0pVUj09XCJ1bmRlZmluZWRcInx8IUtKVVIpe0tKVVI9e319aWYodHlwZW9mIEtKVVIubGFuZz09XCJ1bmRlZmluZWRcInx8IUtKVVIubGFuZyl7S0pVUi5sYW5nPXt9fUtKVVIubGFuZy5TdHJpbmc9ZnVuY3Rpb24oKXt9O2Z1bmN0aW9uIEJhc2U2NHgoKXt9ZnVuY3Rpb24gc3RvQkEoZCl7dmFyIGI9bmV3IEFycmF5KCk7Zm9yKHZhciBjPTA7YzxkLmxlbmd0aDtjKyspe2JbY109ZC5jaGFyQ29kZUF0KGMpfXJldHVybiBifWZ1bmN0aW9uIEJBdG9zKGIpe3ZhciBkPVwiXCI7Zm9yKHZhciBjPTA7YzxiLmxlbmd0aDtjKyspe2Q9ZCtTdHJpbmcuZnJvbUNoYXJDb2RlKGJbY10pfXJldHVybiBkfWZ1bmN0aW9uIEJBdG9oZXgoYil7dmFyIGU9XCJcIjtmb3IodmFyIGQ9MDtkPGIubGVuZ3RoO2QrKyl7dmFyIGM9YltkXS50b1N0cmluZygxNik7aWYoYy5sZW5ndGg9PTEpe2M9XCIwXCIrY31lPWUrY31yZXR1cm4gZX1mdW5jdGlvbiBzdG9oZXgoYSl7cmV0dXJuIEJBdG9oZXgoc3RvQkEoYSkpfWZ1bmN0aW9uIHN0b2I2NChhKXtyZXR1cm4gaGV4MmI2NChzdG9oZXgoYSkpfWZ1bmN0aW9uIHN0b2I2NHUoYSl7cmV0dXJuIGI2NHRvYjY0dShoZXgyYjY0KHN0b2hleChhKSkpfWZ1bmN0aW9uIGI2NHV0b3MoYSl7cmV0dXJuIEJBdG9zKGI2NHRvQkEoYjY0dXRvYjY0KGEpKSl9ZnVuY3Rpb24gYjY0dG9iNjR1KGEpe2E9YS5yZXBsYWNlKC9cXD0vZyxcIlwiKTthPWEucmVwbGFjZSgvXFwrL2csXCItXCIpO2E9YS5yZXBsYWNlKC9cXC8vZyxcIl9cIik7cmV0dXJuIGF9ZnVuY3Rpb24gYjY0dXRvYjY0KGEpe2lmKGEubGVuZ3RoJTQ9PTIpe2E9YStcIj09XCJ9ZWxzZXtpZihhLmxlbmd0aCU0PT0zKXthPWErXCI9XCJ9fWE9YS5yZXBsYWNlKC8tL2csXCIrXCIpO2E9YS5yZXBsYWNlKC9fL2csXCIvXCIpO3JldHVybiBhfWZ1bmN0aW9uIGhleHRvYjY0dShhKXtpZihhLmxlbmd0aCUyPT0xKXthPVwiMFwiK2F9cmV0dXJuIGI2NHRvYjY0dShoZXgyYjY0KGEpKX1mdW5jdGlvbiBiNjR1dG9oZXgoYSl7cmV0dXJuIGI2NHRvaGV4KGI2NHV0b2I2NChhKSl9dmFyIHV0Zjh0b2I2NHUsYjY0dXRvdXRmODtpZih0eXBlb2YgQnVmZmVyPT09XCJmdW5jdGlvblwiKXt1dGY4dG9iNjR1PWZ1bmN0aW9uKGEpe3JldHVybiBiNjR0b2I2NHUobmV3IEJ1ZmZlcihhLFwidXRmOFwiKS50b1N0cmluZyhcImJhc2U2NFwiKSl9O2I2NHV0b3V0Zjg9ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBCdWZmZXIoYjY0dXRvYjY0KGEpLFwiYmFzZTY0XCIpLnRvU3RyaW5nKFwidXRmOFwiKX19ZWxzZXt1dGY4dG9iNjR1PWZ1bmN0aW9uKGEpe3JldHVybiBoZXh0b2I2NHUodXJpY21wdG9oZXgoZW5jb2RlVVJJQ29tcG9uZW50QWxsKGEpKSl9O2I2NHV0b3V0Zjg9ZnVuY3Rpb24oYSl7cmV0dXJuIGRlY29kZVVSSUNvbXBvbmVudChoZXh0b3VyaWNtcChiNjR1dG9oZXgoYSkpKX19ZnVuY3Rpb24gdXRmOHRvYjY0KGEpe3JldHVybiBoZXgyYjY0KHVyaWNtcHRvaGV4KGVuY29kZVVSSUNvbXBvbmVudEFsbChhKSkpfWZ1bmN0aW9uIGI2NHRvdXRmOChhKXtyZXR1cm4gZGVjb2RlVVJJQ29tcG9uZW50KGhleHRvdXJpY21wKGI2NHRvaGV4KGEpKSl9ZnVuY3Rpb24gdXRmOHRvaGV4KGEpe3JldHVybiB1cmljbXB0b2hleChlbmNvZGVVUklDb21wb25lbnRBbGwoYSkpfWZ1bmN0aW9uIGhleHRvdXRmOChhKXtyZXR1cm4gZGVjb2RlVVJJQ29tcG9uZW50KGhleHRvdXJpY21wKGEpKX1mdW5jdGlvbiBoZXh0b3JzdHIoYyl7dmFyIGI9XCJcIjtmb3IodmFyIGE9MDthPGMubGVuZ3RoLTE7YSs9Mil7Yis9U3RyaW5nLmZyb21DaGFyQ29kZShwYXJzZUludChjLnN1YnN0cihhLDIpLDE2KSl9cmV0dXJuIGJ9ZnVuY3Rpb24gcnN0cnRvaGV4KGMpe3ZhciBhPVwiXCI7Zm9yKHZhciBiPTA7YjxjLmxlbmd0aDtiKyspe2ErPShcIjBcIitjLmNoYXJDb2RlQXQoYikudG9TdHJpbmcoMTYpKS5zbGljZSgtMil9cmV0dXJuIGF9ZnVuY3Rpb24gaGV4dG9iNjQoYSl7cmV0dXJuIGhleDJiNjQoYSl9ZnVuY3Rpb24gaGV4dG9iNjRubChiKXt2YXIgYT1oZXh0b2I2NChiKTt2YXIgYz1hLnJlcGxhY2UoLyguezY0fSkvZyxcIiQxXFxyXFxuXCIpO2M9Yy5yZXBsYWNlKC9cXHJcXG4kLyxcIlwiKTtyZXR1cm4gY31mdW5jdGlvbiBiNjRubHRvaGV4KGIpe3ZhciBhPWIucmVwbGFjZSgvW14wLTlBLVphLXpcXC8rPV0qL2csXCJcIik7dmFyIGM9YjY0dG9oZXgoYSk7cmV0dXJuIGN9ZnVuY3Rpb24gaGV4dG9wZW0oYSxiKXt2YXIgYz1oZXh0b2I2NG5sKGEpO3JldHVyblwiLS0tLS1CRUdJTiBcIitiK1wiLS0tLS1cXHJcXG5cIitjK1wiXFxyXFxuLS0tLS1FTkQgXCIrYitcIi0tLS0tXFxyXFxuXCJ9ZnVuY3Rpb24gcGVtdG9oZXgoYSxiKXtpZihhLmluZGV4T2YoXCItLS0tLUJFR0lOIFwiKT09LTEpe3Rocm93XCJjYW4ndCBmaW5kIFBFTSBoZWFkZXI6IFwiK2J9aWYoYiE9PXVuZGVmaW5lZCl7YT1hLnJlcGxhY2UoXCItLS0tLUJFR0lOIFwiK2IrXCItLS0tLVwiLFwiXCIpO2E9YS5yZXBsYWNlKFwiLS0tLS1FTkQgXCIrYitcIi0tLS0tXCIsXCJcIil9ZWxzZXthPWEucmVwbGFjZSgvLS0tLS1CRUdJTiBbXi1dKy0tLS0tLyxcIlwiKTthPWEucmVwbGFjZSgvLS0tLS1FTkQgW14tXSstLS0tLS8sXCJcIil9cmV0dXJuIGI2NG5sdG9oZXgoYSl9ZnVuY3Rpb24gaGV4dG9BcnJheUJ1ZmZlcihkKXtpZihkLmxlbmd0aCUyIT0wKXt0aHJvd1wiaW5wdXQgaXMgbm90IGV2ZW4gbGVuZ3RoXCJ9aWYoZC5tYXRjaCgvXlswLTlBLUZhLWZdKyQvKT09bnVsbCl7dGhyb3dcImlucHV0IGlzIG5vdCBoZXhhZGVjaW1hbFwifXZhciBiPW5ldyBBcnJheUJ1ZmZlcihkLmxlbmd0aC8yKTt2YXIgYT1uZXcgRGF0YVZpZXcoYik7Zm9yKHZhciBjPTA7YzxkLmxlbmd0aC8yO2MrKyl7YS5zZXRVaW50OChjLHBhcnNlSW50KGQuc3Vic3RyKGMqMiwyKSwxNikpfXJldHVybiBifWZ1bmN0aW9uIEFycmF5QnVmZmVydG9oZXgoYil7dmFyIGQ9XCJcIjt2YXIgYT1uZXcgRGF0YVZpZXcoYik7Zm9yKHZhciBjPTA7YzxiLmJ5dGVMZW5ndGg7YysrKXtkKz0oXCIwMFwiK2EuZ2V0VWludDgoYykudG9TdHJpbmcoMTYpKS5zbGljZSgtMil9cmV0dXJuIGR9ZnVuY3Rpb24genVsdXRvbXNlYyhuKXt2YXIgbCxqLG0sZSxmLGksYixrO3ZhciBhLGgsZyxjO2M9bi5tYXRjaCgvXihcXGR7Mn18XFxkezR9KShcXGRcXGQpKFxcZFxcZCkoXFxkXFxkKShcXGRcXGQpKFxcZFxcZCkofFxcLlxcZCspWiQvKTtpZihjKXthPWNbMV07bD1wYXJzZUludChhKTtpZihhLmxlbmd0aD09PTIpe2lmKDUwPD1sJiZsPDEwMCl7bD0xOTAwK2x9ZWxzZXtpZigwPD1sJiZsPDUwKXtsPTIwMDArbH19fWo9cGFyc2VJbnQoY1syXSktMTttPXBhcnNlSW50KGNbM10pO2U9cGFyc2VJbnQoY1s0XSk7Zj1wYXJzZUludChjWzVdKTtpPXBhcnNlSW50KGNbNl0pO2I9MDtoPWNbN107aWYoaCE9PVwiXCIpe2c9KGguc3Vic3RyKDEpK1wiMDBcIikuc3Vic3RyKDAsMyk7Yj1wYXJzZUludChnKX1yZXR1cm4gRGF0ZS5VVEMobCxqLG0sZSxmLGksYil9dGhyb3dcInVuc3VwcG9ydGVkIHp1bHUgZm9ybWF0OiBcIitufWZ1bmN0aW9uIHp1bHV0b3NlYyhhKXt2YXIgYj16dWx1dG9tc2VjKGEpO3JldHVybiB+fihiLzEwMDApfWZ1bmN0aW9uIHp1bHV0b2RhdGUoYSl7cmV0dXJuIG5ldyBEYXRlKHp1bHV0b21zZWMoYSkpfWZ1bmN0aW9uIGRhdGV0b3p1bHUoZyxlLGYpe3ZhciBiO3ZhciBhPWcuZ2V0VVRDRnVsbFllYXIoKTtpZihlKXtpZihhPDE5NTB8fDIwNDk8YSl7dGhyb3dcIm5vdCBwcm9wZXIgeWVhciBmb3IgVVRDVGltZTogXCIrYX1iPShcIlwiK2EpLnNsaWNlKC0yKX1lbHNle2I9KFwiMDAwXCIrYSkuc2xpY2UoLTQpfWIrPShcIjBcIisoZy5nZXRVVENNb250aCgpKzEpKS5zbGljZSgtMik7Yis9KFwiMFwiK2cuZ2V0VVRDRGF0ZSgpKS5zbGljZSgtMik7Yis9KFwiMFwiK2cuZ2V0VVRDSG91cnMoKSkuc2xpY2UoLTIpO2IrPShcIjBcIitnLmdldFVUQ01pbnV0ZXMoKSkuc2xpY2UoLTIpO2IrPShcIjBcIitnLmdldFVUQ1NlY29uZHMoKSkuc2xpY2UoLTIpO2lmKGYpe3ZhciBjPWcuZ2V0VVRDTWlsbGlzZWNvbmRzKCk7aWYoYyE9PTApe2M9KFwiMDBcIitjKS5zbGljZSgtMyk7Yz1jLnJlcGxhY2UoLzArJC9nLFwiXCIpO2IrPVwiLlwiK2N9fWIrPVwiWlwiO3JldHVybiBifWZ1bmN0aW9uIHVyaWNtcHRvaGV4KGEpe3JldHVybiBhLnJlcGxhY2UoLyUvZyxcIlwiKX1mdW5jdGlvbiBoZXh0b3VyaWNtcChhKXtyZXR1cm4gYS5yZXBsYWNlKC8oLi4pL2csXCIlJDFcIil9ZnVuY3Rpb24gaXB2NnRvaGV4KGcpe3ZhciBiPVwibWFsZm9ybWVkIElQdjYgYWRkcmVzc1wiO2lmKCFnLm1hdGNoKC9eWzAtOUEtRmEtZjpdKyQvKSl7dGhyb3cgYn1nPWcudG9Mb3dlckNhc2UoKTt2YXIgZD1nLnNwbGl0KFwiOlwiKS5sZW5ndGgtMTtpZihkPDIpe3Rocm93IGJ9dmFyIGU9XCI6XCIucmVwZWF0KDctZCsyKTtnPWcucmVwbGFjZShcIjo6XCIsZSk7dmFyIGM9Zy5zcGxpdChcIjpcIik7aWYoYy5sZW5ndGghPTgpe3Rocm93IGJ9Zm9yKHZhciBmPTA7Zjw4O2YrKyl7Y1tmXT0oXCIwMDAwXCIrY1tmXSkuc2xpY2UoLTQpfXJldHVybiBjLmpvaW4oXCJcIil9ZnVuY3Rpb24gaGV4dG9pcHY2KGUpe2lmKCFlLm1hdGNoKC9eWzAtOUEtRmEtZl17MzJ9JC8pKXt0aHJvd1wibWFsZm9ybWVkIElQdjYgYWRkcmVzcyBvY3RldFwifWU9ZS50b0xvd2VyQ2FzZSgpO3ZhciBiPWUubWF0Y2goLy57MSw0fS9nKTtmb3IodmFyIGQ9MDtkPDg7ZCsrKXtiW2RdPWJbZF0ucmVwbGFjZSgvXjArLyxcIlwiKTtpZihiW2RdPT1cIlwiKXtiW2RdPVwiMFwifX1lPVwiOlwiK2Iuam9pbihcIjpcIikrXCI6XCI7dmFyIGM9ZS5tYXRjaCgvOigwOil7Mix9L2cpO2lmKGM9PT1udWxsKXtyZXR1cm4gZS5zbGljZSgxLC0xKX12YXIgZj1cIlwiO2Zvcih2YXIgZD0wO2Q8Yy5sZW5ndGg7ZCsrKXtpZihjW2RdLmxlbmd0aD5mLmxlbmd0aCl7Zj1jW2RdfX1lPWUucmVwbGFjZShmLFwiOjpcIik7cmV0dXJuIGUuc2xpY2UoMSwtMSl9ZnVuY3Rpb24gaGV4dG9pcChiKXt2YXIgZD1cIm1hbGZvcm1lZCBoZXggdmFsdWVcIjtpZighYi5tYXRjaCgvXihbMC05QS1GYS1mXVswLTlBLUZhLWZdKXsxLH0kLykpe3Rocm93IGR9aWYoYi5sZW5ndGg9PTgpe3ZhciBjO3RyeXtjPXBhcnNlSW50KGIuc3Vic3RyKDAsMiksMTYpK1wiLlwiK3BhcnNlSW50KGIuc3Vic3RyKDIsMiksMTYpK1wiLlwiK3BhcnNlSW50KGIuc3Vic3RyKDQsMiksMTYpK1wiLlwiK3BhcnNlSW50KGIuc3Vic3RyKDYsMiksMTYpO3JldHVybiBjfWNhdGNoKGEpe3Rocm93IGR9fWVsc2V7aWYoYi5sZW5ndGg9PTMyKXtyZXR1cm4gaGV4dG9pcHY2KGIpfWVsc2V7cmV0dXJuIGJ9fX1mdW5jdGlvbiBpcHRvaGV4KGYpe3ZhciBqPVwibWFsZm9ybWVkIElQIGFkZHJlc3NcIjtmPWYudG9Mb3dlckNhc2UoZik7aWYoZi5tYXRjaCgvXlswLTkuXSskLykpe3ZhciBiPWYuc3BsaXQoXCIuXCIpO2lmKGIubGVuZ3RoIT09NCl7dGhyb3cgan12YXIgZz1cIlwiO3RyeXtmb3IodmFyIGU9MDtlPDQ7ZSsrKXt2YXIgaD1wYXJzZUludChiW2VdKTtnKz0oXCIwXCIraC50b1N0cmluZygxNikpLnNsaWNlKC0yKX1yZXR1cm4gZ31jYXRjaChjKXt0aHJvdyBqfX1lbHNle2lmKGYubWF0Y2goL15bMC05YS1mOl0rJC8pJiZmLmluZGV4T2YoXCI6XCIpIT09LTEpe3JldHVybiBpcHY2dG9oZXgoZil9ZWxzZXt0aHJvdyBqfX19ZnVuY3Rpb24gZW5jb2RlVVJJQ29tcG9uZW50QWxsKGEpe3ZhciBkPWVuY29kZVVSSUNvbXBvbmVudChhKTt2YXIgYj1cIlwiO2Zvcih2YXIgYz0wO2M8ZC5sZW5ndGg7YysrKXtpZihkW2NdPT1cIiVcIil7Yj1iK2Quc3Vic3RyKGMsMyk7Yz1jKzJ9ZWxzZXtiPWIrXCIlXCIrc3RvaGV4KGRbY10pfX1yZXR1cm4gYn1mdW5jdGlvbiBuZXdsaW5lX3RvVW5peChhKXthPWEucmVwbGFjZSgvXFxyXFxuL21nLFwiXFxuXCIpO3JldHVybiBhfWZ1bmN0aW9uIG5ld2xpbmVfdG9Eb3MoYSl7YT1hLnJlcGxhY2UoL1xcclxcbi9tZyxcIlxcblwiKTthPWEucmVwbGFjZSgvXFxuL21nLFwiXFxyXFxuXCIpO3JldHVybiBhfUtKVVIubGFuZy5TdHJpbmcuaXNJbnRlZ2VyPWZ1bmN0aW9uKGEpe2lmKGEubWF0Y2goL15bMC05XSskLykpe3JldHVybiB0cnVlfWVsc2V7aWYoYS5tYXRjaCgvXi1bMC05XSskLykpe3JldHVybiB0cnVlfWVsc2V7cmV0dXJuIGZhbHNlfX19O0tKVVIubGFuZy5TdHJpbmcuaXNIZXg9ZnVuY3Rpb24oYSl7aWYoYS5sZW5ndGglMj09MCYmKGEubWF0Y2goL15bMC05YS1mXSskLyl8fGEubWF0Y2goL15bMC05QS1GXSskLykpKXtyZXR1cm4gdHJ1ZX1lbHNle3JldHVybiBmYWxzZX19O0tKVVIubGFuZy5TdHJpbmcuaXNCYXNlNjQ9ZnVuY3Rpb24oYSl7YT1hLnJlcGxhY2UoL1xccysvZyxcIlwiKTtpZihhLm1hdGNoKC9eWzAtOUEtWmEteitcXC9dKz17MCwzfSQvKSYmYS5sZW5ndGglND09MCl7cmV0dXJuIHRydWV9ZWxzZXtyZXR1cm4gZmFsc2V9fTtLSlVSLmxhbmcuU3RyaW5nLmlzQmFzZTY0VVJMPWZ1bmN0aW9uKGEpe2lmKGEubWF0Y2goL1srLz1dLykpe3JldHVybiBmYWxzZX1hPWI2NHV0b2I2NChhKTtyZXR1cm4gS0pVUi5sYW5nLlN0cmluZy5pc0Jhc2U2NChhKX07S0pVUi5sYW5nLlN0cmluZy5pc0ludGVnZXJBcnJheT1mdW5jdGlvbihhKXthPWEucmVwbGFjZSgvXFxzKy9nLFwiXCIpO2lmKGEubWF0Y2goL15cXFtbMC05LF0rXFxdJC8pKXtyZXR1cm4gdHJ1ZX1lbHNle3JldHVybiBmYWxzZX19O2Z1bmN0aW9uIGhleHRvcG9zaGV4KGEpe2lmKGEubGVuZ3RoJTI9PTEpe3JldHVyblwiMFwiK2F9aWYoYS5zdWJzdHIoMCwxKT5cIjdcIil7cmV0dXJuXCIwMFwiK2F9cmV0dXJuIGF9ZnVuY3Rpb24gaW50YXJ5c3RydG9oZXgoYil7Yj1iLnJlcGxhY2UoL15cXHMqXFxbXFxzKi8sXCJcIik7Yj1iLnJlcGxhY2UoL1xccypcXF1cXHMqJC8sXCJcIik7Yj1iLnJlcGxhY2UoL1xccyovZyxcIlwiKTt0cnl7dmFyIGM9Yi5zcGxpdCgvLC8pLm1hcChmdW5jdGlvbihnLGUsaCl7dmFyIGY9cGFyc2VJbnQoZyk7aWYoZjwwfHwyNTU8Zil7dGhyb3dcImludGVnZXIgbm90IGluIHJhbmdlIDAtMjU1XCJ9dmFyIGQ9KFwiMDBcIitmLnRvU3RyaW5nKDE2KSkuc2xpY2UoLTIpO3JldHVybiBkfSkuam9pbihcIlwiKTtyZXR1cm4gY31jYXRjaChhKXt0aHJvd1wibWFsZm9ybWVkIGludGVnZXIgYXJyYXkgc3RyaW5nOiBcIithfX12YXIgc3RyZGlmZmlkeD1mdW5jdGlvbihjLGEpe3ZhciBkPWMubGVuZ3RoO2lmKGMubGVuZ3RoPmEubGVuZ3RoKXtkPWEubGVuZ3RofWZvcih2YXIgYj0wO2I8ZDtiKyspe2lmKGMuY2hhckNvZGVBdChiKSE9YS5jaGFyQ29kZUF0KGIpKXtyZXR1cm4gYn19aWYoYy5sZW5ndGghPWEubGVuZ3RoKXtyZXR1cm4gZH1yZXR1cm4gLTF9O1xuaWYodHlwZW9mIEtKVVI9PVwidW5kZWZpbmVkXCJ8fCFLSlVSKXtLSlVSPXt9fWlmKHR5cGVvZiBLSlVSLmNyeXB0bz09XCJ1bmRlZmluZWRcInx8IUtKVVIuY3J5cHRvKXtLSlVSLmNyeXB0bz17fX1LSlVSLmNyeXB0by5VdGlsPW5ldyBmdW5jdGlvbigpe3RoaXMuRElHRVNUSU5GT0hFQUQ9e3NoYTE6XCIzMDIxMzAwOTA2MDUyYjBlMDMwMjFhMDUwMDA0MTRcIixzaGEyMjQ6XCIzMDJkMzAwZDA2MDk2MDg2NDgwMTY1MDMwNDAyMDQwNTAwMDQxY1wiLHNoYTI1NjpcIjMwMzEzMDBkMDYwOTYwODY0ODAxNjUwMzA0MDIwMTA1MDAwNDIwXCIsc2hhMzg0OlwiMzA0MTMwMGQwNjA5NjA4NjQ4MDE2NTAzMDQwMjAyMDUwMDA0MzBcIixzaGE1MTI6XCIzMDUxMzAwZDA2MDk2MDg2NDgwMTY1MDMwNDAyMDMwNTAwMDQ0MFwiLG1kMjpcIjMwMjAzMDBjMDYwODJhODY0ODg2ZjcwZDAyMDIwNTAwMDQxMFwiLG1kNTpcIjMwMjAzMDBjMDYwODJhODY0ODg2ZjcwZDAyMDUwNTAwMDQxMFwiLHJpcGVtZDE2MDpcIjMwMjEzMDA5MDYwNTJiMjQwMzAyMDEwNTAwMDQxNFwiLH07dGhpcy5ERUZBVUxUUFJPVklERVI9e21kNTpcImNyeXB0b2pzXCIsc2hhMTpcImNyeXB0b2pzXCIsc2hhMjI0OlwiY3J5cHRvanNcIixzaGEyNTY6XCJjcnlwdG9qc1wiLHNoYTM4NDpcImNyeXB0b2pzXCIsc2hhNTEyOlwiY3J5cHRvanNcIixyaXBlbWQxNjA6XCJjcnlwdG9qc1wiLGhtYWNtZDU6XCJjcnlwdG9qc1wiLGhtYWNzaGExOlwiY3J5cHRvanNcIixobWFjc2hhMjI0OlwiY3J5cHRvanNcIixobWFjc2hhMjU2OlwiY3J5cHRvanNcIixobWFjc2hhMzg0OlwiY3J5cHRvanNcIixobWFjc2hhNTEyOlwiY3J5cHRvanNcIixobWFjcmlwZW1kMTYwOlwiY3J5cHRvanNcIixNRDV3aXRoUlNBOlwiY3J5cHRvanMvanNyc2FcIixTSEExd2l0aFJTQTpcImNyeXB0b2pzL2pzcnNhXCIsU0hBMjI0d2l0aFJTQTpcImNyeXB0b2pzL2pzcnNhXCIsU0hBMjU2d2l0aFJTQTpcImNyeXB0b2pzL2pzcnNhXCIsU0hBMzg0d2l0aFJTQTpcImNyeXB0b2pzL2pzcnNhXCIsU0hBNTEyd2l0aFJTQTpcImNyeXB0b2pzL2pzcnNhXCIsUklQRU1EMTYwd2l0aFJTQTpcImNyeXB0b2pzL2pzcnNhXCIsTUQ1d2l0aEVDRFNBOlwiY3J5cHRvanMvanNyc2FcIixTSEExd2l0aEVDRFNBOlwiY3J5cHRvanMvanNyc2FcIixTSEEyMjR3aXRoRUNEU0E6XCJjcnlwdG9qcy9qc3JzYVwiLFNIQTI1NndpdGhFQ0RTQTpcImNyeXB0b2pzL2pzcnNhXCIsU0hBMzg0d2l0aEVDRFNBOlwiY3J5cHRvanMvanNyc2FcIixTSEE1MTJ3aXRoRUNEU0E6XCJjcnlwdG9qcy9qc3JzYVwiLFJJUEVNRDE2MHdpdGhFQ0RTQTpcImNyeXB0b2pzL2pzcnNhXCIsU0hBMXdpdGhEU0E6XCJjcnlwdG9qcy9qc3JzYVwiLFNIQTIyNHdpdGhEU0E6XCJjcnlwdG9qcy9qc3JzYVwiLFNIQTI1NndpdGhEU0E6XCJjcnlwdG9qcy9qc3JzYVwiLE1ENXdpdGhSU0FhbmRNR0YxOlwiY3J5cHRvanMvanNyc2FcIixTSEExd2l0aFJTQWFuZE1HRjE6XCJjcnlwdG9qcy9qc3JzYVwiLFNIQTIyNHdpdGhSU0FhbmRNR0YxOlwiY3J5cHRvanMvanNyc2FcIixTSEEyNTZ3aXRoUlNBYW5kTUdGMTpcImNyeXB0b2pzL2pzcnNhXCIsU0hBMzg0d2l0aFJTQWFuZE1HRjE6XCJjcnlwdG9qcy9qc3JzYVwiLFNIQTUxMndpdGhSU0FhbmRNR0YxOlwiY3J5cHRvanMvanNyc2FcIixSSVBFTUQxNjB3aXRoUlNBYW5kTUdGMTpcImNyeXB0b2pzL2pzcnNhXCIsfTt0aGlzLkNSWVBUT0pTTUVTU0FHRURJR0VTVE5BTUU9e21kNTpDcnlwdG9KUy5hbGdvLk1ENSxzaGExOkNyeXB0b0pTLmFsZ28uU0hBMSxzaGEyMjQ6Q3J5cHRvSlMuYWxnby5TSEEyMjQsc2hhMjU2OkNyeXB0b0pTLmFsZ28uU0hBMjU2LHNoYTM4NDpDcnlwdG9KUy5hbGdvLlNIQTM4NCxzaGE1MTI6Q3J5cHRvSlMuYWxnby5TSEE1MTIscmlwZW1kMTYwOkNyeXB0b0pTLmFsZ28uUklQRU1EMTYwfTt0aGlzLmdldERpZ2VzdEluZm9IZXg9ZnVuY3Rpb24oYSxiKXtpZih0eXBlb2YgdGhpcy5ESUdFU1RJTkZPSEVBRFtiXT09XCJ1bmRlZmluZWRcIil7dGhyb3dcImFsZyBub3Qgc3VwcG9ydGVkIGluIFV0aWwuRElHRVNUSU5GT0hFQUQ6IFwiK2J9cmV0dXJuIHRoaXMuRElHRVNUSU5GT0hFQURbYl0rYX07dGhpcy5nZXRQYWRkZWREaWdlc3RJbmZvSGV4PWZ1bmN0aW9uKGgsYSxqKXt2YXIgYz10aGlzLmdldERpZ2VzdEluZm9IZXgoaCxhKTt2YXIgZD1qLzQ7aWYoYy5sZW5ndGgrMjI+ZCl7dGhyb3dcImtleSBpcyB0b28gc2hvcnQgZm9yIFNpZ0FsZzoga2V5bGVuPVwiK2orXCIsXCIrYX12YXIgYj1cIjAwMDFcIjt2YXIgaz1cIjAwXCIrYzt2YXIgZz1cIlwiO3ZhciBsPWQtYi5sZW5ndGgtay5sZW5ndGg7Zm9yKHZhciBmPTA7ZjxsO2YrPTIpe2crPVwiZmZcIn12YXIgZT1iK2craztyZXR1cm4gZX07dGhpcy5oYXNoU3RyaW5nPWZ1bmN0aW9uKGEsYyl7dmFyIGI9bmV3IEtKVVIuY3J5cHRvLk1lc3NhZ2VEaWdlc3Qoe2FsZzpjfSk7cmV0dXJuIGIuZGlnZXN0U3RyaW5nKGEpfTt0aGlzLmhhc2hIZXg9ZnVuY3Rpb24oYixjKXt2YXIgYT1uZXcgS0pVUi5jcnlwdG8uTWVzc2FnZURpZ2VzdCh7YWxnOmN9KTtyZXR1cm4gYS5kaWdlc3RIZXgoYil9O3RoaXMuc2hhMT1mdW5jdGlvbihhKXt2YXIgYj1uZXcgS0pVUi5jcnlwdG8uTWVzc2FnZURpZ2VzdCh7YWxnOlwic2hhMVwiLHByb3Y6XCJjcnlwdG9qc1wifSk7cmV0dXJuIGIuZGlnZXN0U3RyaW5nKGEpfTt0aGlzLnNoYTI1Nj1mdW5jdGlvbihhKXt2YXIgYj1uZXcgS0pVUi5jcnlwdG8uTWVzc2FnZURpZ2VzdCh7YWxnOlwic2hhMjU2XCIscHJvdjpcImNyeXB0b2pzXCJ9KTtyZXR1cm4gYi5kaWdlc3RTdHJpbmcoYSl9O3RoaXMuc2hhMjU2SGV4PWZ1bmN0aW9uKGEpe3ZhciBiPW5ldyBLSlVSLmNyeXB0by5NZXNzYWdlRGlnZXN0KHthbGc6XCJzaGEyNTZcIixwcm92OlwiY3J5cHRvanNcIn0pO3JldHVybiBiLmRpZ2VzdEhleChhKX07dGhpcy5zaGE1MTI9ZnVuY3Rpb24oYSl7dmFyIGI9bmV3IEtKVVIuY3J5cHRvLk1lc3NhZ2VEaWdlc3Qoe2FsZzpcInNoYTUxMlwiLHByb3Y6XCJjcnlwdG9qc1wifSk7cmV0dXJuIGIuZGlnZXN0U3RyaW5nKGEpfTt0aGlzLnNoYTUxMkhleD1mdW5jdGlvbihhKXt2YXIgYj1uZXcgS0pVUi5jcnlwdG8uTWVzc2FnZURpZ2VzdCh7YWxnOlwic2hhNTEyXCIscHJvdjpcImNyeXB0b2pzXCJ9KTtyZXR1cm4gYi5kaWdlc3RIZXgoYSl9fTtLSlVSLmNyeXB0by5VdGlsLm1kNT1mdW5jdGlvbihhKXt2YXIgYj1uZXcgS0pVUi5jcnlwdG8uTWVzc2FnZURpZ2VzdCh7YWxnOlwibWQ1XCIscHJvdjpcImNyeXB0b2pzXCJ9KTtyZXR1cm4gYi5kaWdlc3RTdHJpbmcoYSl9O0tKVVIuY3J5cHRvLlV0aWwucmlwZW1kMTYwPWZ1bmN0aW9uKGEpe3ZhciBiPW5ldyBLSlVSLmNyeXB0by5NZXNzYWdlRGlnZXN0KHthbGc6XCJyaXBlbWQxNjBcIixwcm92OlwiY3J5cHRvanNcIn0pO3JldHVybiBiLmRpZ2VzdFN0cmluZyhhKX07S0pVUi5jcnlwdG8uVXRpbC5TRUNVUkVSQU5ET01HRU49bmV3IFNlY3VyZVJhbmRvbSgpO0tKVVIuY3J5cHRvLlV0aWwuZ2V0UmFuZG9tSGV4T2ZOYnl0ZXM9ZnVuY3Rpb24oYil7dmFyIGE9bmV3IEFycmF5KGIpO0tKVVIuY3J5cHRvLlV0aWwuU0VDVVJFUkFORE9NR0VOLm5leHRCeXRlcyhhKTtyZXR1cm4gQkF0b2hleChhKX07S0pVUi5jcnlwdG8uVXRpbC5nZXRSYW5kb21CaWdJbnRlZ2VyT2ZOYnl0ZXM9ZnVuY3Rpb24oYSl7cmV0dXJuIG5ldyBCaWdJbnRlZ2VyKEtKVVIuY3J5cHRvLlV0aWwuZ2V0UmFuZG9tSGV4T2ZOYnl0ZXMoYSksMTYpfTtLSlVSLmNyeXB0by5VdGlsLmdldFJhbmRvbUhleE9mTmJpdHM9ZnVuY3Rpb24oZCl7dmFyIGM9ZCU4O3ZhciBhPShkLWMpLzg7dmFyIGI9bmV3IEFycmF5KGErMSk7S0pVUi5jcnlwdG8uVXRpbC5TRUNVUkVSQU5ET01HRU4ubmV4dEJ5dGVzKGIpO2JbMF09KCgoMjU1PDxjKSYyNTUpXjI1NSkmYlswXTtyZXR1cm4gQkF0b2hleChiKX07S0pVUi5jcnlwdG8uVXRpbC5nZXRSYW5kb21CaWdJbnRlZ2VyT2ZOYml0cz1mdW5jdGlvbihhKXtyZXR1cm4gbmV3IEJpZ0ludGVnZXIoS0pVUi5jcnlwdG8uVXRpbC5nZXRSYW5kb21IZXhPZk5iaXRzKGEpLDE2KX07S0pVUi5jcnlwdG8uVXRpbC5nZXRSYW5kb21CaWdJbnRlZ2VyWmVyb1RvTWF4PWZ1bmN0aW9uKGIpe3ZhciBhPWIuYml0TGVuZ3RoKCk7d2hpbGUoMSl7dmFyIGM9S0pVUi5jcnlwdG8uVXRpbC5nZXRSYW5kb21CaWdJbnRlZ2VyT2ZOYml0cyhhKTtpZihiLmNvbXBhcmVUbyhjKSE9LTEpe3JldHVybiBjfX19O0tKVVIuY3J5cHRvLlV0aWwuZ2V0UmFuZG9tQmlnSW50ZWdlck1pblRvTWF4PWZ1bmN0aW9uKGUsYil7dmFyIGM9ZS5jb21wYXJlVG8oYik7aWYoYz09MSl7dGhyb3dcImJpTWluIGlzIGdyZWF0ZXIgdGhhbiBiaU1heFwifWlmKGM9PTApe3JldHVybiBlfXZhciBhPWIuc3VidHJhY3QoZSk7dmFyIGQ9S0pVUi5jcnlwdG8uVXRpbC5nZXRSYW5kb21CaWdJbnRlZ2VyWmVyb1RvTWF4KGEpO3JldHVybiBkLmFkZChlKX07S0pVUi5jcnlwdG8uTWVzc2FnZURpZ2VzdD1mdW5jdGlvbihjKXt2YXIgYj1udWxsO3ZhciBhPW51bGw7dmFyIGQ9bnVsbDt0aGlzLnNldEFsZ0FuZFByb3ZpZGVyPWZ1bmN0aW9uKGcsZil7Zz1LSlVSLmNyeXB0by5NZXNzYWdlRGlnZXN0LmdldENhbm9uaWNhbEFsZ05hbWUoZyk7aWYoZyE9PW51bGwmJmY9PT11bmRlZmluZWQpe2Y9S0pVUi5jcnlwdG8uVXRpbC5ERUZBVUxUUFJPVklERVJbZ119aWYoXCI6bWQ1OnNoYTE6c2hhMjI0OnNoYTI1NjpzaGEzODQ6c2hhNTEyOnJpcGVtZDE2MDpcIi5pbmRleE9mKGcpIT0tMSYmZj09XCJjcnlwdG9qc1wiKXt0cnl7dGhpcy5tZD1LSlVSLmNyeXB0by5VdGlsLkNSWVBUT0pTTUVTU0FHRURJR0VTVE5BTUVbZ10uY3JlYXRlKCl9Y2F0Y2goZSl7dGhyb3dcInNldEFsZ0FuZFByb3ZpZGVyIGhhc2ggYWxnIHNldCBmYWlsIGFsZz1cIitnK1wiL1wiK2V9dGhpcy51cGRhdGVTdHJpbmc9ZnVuY3Rpb24oaCl7dGhpcy5tZC51cGRhdGUoaCl9O3RoaXMudXBkYXRlSGV4PWZ1bmN0aW9uKGgpe3ZhciBpPUNyeXB0b0pTLmVuYy5IZXgucGFyc2UoaCk7dGhpcy5tZC51cGRhdGUoaSl9O3RoaXMuZGlnZXN0PWZ1bmN0aW9uKCl7dmFyIGg9dGhpcy5tZC5maW5hbGl6ZSgpO3JldHVybiBoLnRvU3RyaW5nKENyeXB0b0pTLmVuYy5IZXgpfTt0aGlzLmRpZ2VzdFN0cmluZz1mdW5jdGlvbihoKXt0aGlzLnVwZGF0ZVN0cmluZyhoKTtyZXR1cm4gdGhpcy5kaWdlc3QoKX07dGhpcy5kaWdlc3RIZXg9ZnVuY3Rpb24oaCl7dGhpcy51cGRhdGVIZXgoaCk7cmV0dXJuIHRoaXMuZGlnZXN0KCl9fWlmKFwiOnNoYTI1NjpcIi5pbmRleE9mKGcpIT0tMSYmZj09XCJzamNsXCIpe3RyeXt0aGlzLm1kPW5ldyBzamNsLmhhc2guc2hhMjU2KCl9Y2F0Y2goZSl7dGhyb3dcInNldEFsZ0FuZFByb3ZpZGVyIGhhc2ggYWxnIHNldCBmYWlsIGFsZz1cIitnK1wiL1wiK2V9dGhpcy51cGRhdGVTdHJpbmc9ZnVuY3Rpb24oaCl7dGhpcy5tZC51cGRhdGUoaCl9O3RoaXMudXBkYXRlSGV4PWZ1bmN0aW9uKGkpe3ZhciBoPXNqY2wuY29kZWMuaGV4LnRvQml0cyhpKTt0aGlzLm1kLnVwZGF0ZShoKX07dGhpcy5kaWdlc3Q9ZnVuY3Rpb24oKXt2YXIgaD10aGlzLm1kLmZpbmFsaXplKCk7cmV0dXJuIHNqY2wuY29kZWMuaGV4LmZyb21CaXRzKGgpfTt0aGlzLmRpZ2VzdFN0cmluZz1mdW5jdGlvbihoKXt0aGlzLnVwZGF0ZVN0cmluZyhoKTtyZXR1cm4gdGhpcy5kaWdlc3QoKX07dGhpcy5kaWdlc3RIZXg9ZnVuY3Rpb24oaCl7dGhpcy51cGRhdGVIZXgoaCk7cmV0dXJuIHRoaXMuZGlnZXN0KCl9fX07dGhpcy51cGRhdGVTdHJpbmc9ZnVuY3Rpb24oZSl7dGhyb3dcInVwZGF0ZVN0cmluZyhzdHIpIG5vdCBzdXBwb3J0ZWQgZm9yIHRoaXMgYWxnL3Byb3Y6IFwiK3RoaXMuYWxnTmFtZStcIi9cIit0aGlzLnByb3ZOYW1lfTt0aGlzLnVwZGF0ZUhleD1mdW5jdGlvbihlKXt0aHJvd1widXBkYXRlSGV4KGhleCkgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGcvcHJvdjogXCIrdGhpcy5hbGdOYW1lK1wiL1wiK3RoaXMucHJvdk5hbWV9O3RoaXMuZGlnZXN0PWZ1bmN0aW9uKCl7dGhyb3dcImRpZ2VzdCgpIG5vdCBzdXBwb3J0ZWQgZm9yIHRoaXMgYWxnL3Byb3Y6IFwiK3RoaXMuYWxnTmFtZStcIi9cIit0aGlzLnByb3ZOYW1lfTt0aGlzLmRpZ2VzdFN0cmluZz1mdW5jdGlvbihlKXt0aHJvd1wiZGlnZXN0U3RyaW5nKHN0cikgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGcvcHJvdjogXCIrdGhpcy5hbGdOYW1lK1wiL1wiK3RoaXMucHJvdk5hbWV9O3RoaXMuZGlnZXN0SGV4PWZ1bmN0aW9uKGUpe3Rocm93XCJkaWdlc3RIZXgoaGV4KSBub3Qgc3VwcG9ydGVkIGZvciB0aGlzIGFsZy9wcm92OiBcIit0aGlzLmFsZ05hbWUrXCIvXCIrdGhpcy5wcm92TmFtZX07aWYoYyE9PXVuZGVmaW5lZCl7aWYoYy5hbGchPT11bmRlZmluZWQpe3RoaXMuYWxnTmFtZT1jLmFsZztpZihjLnByb3Y9PT11bmRlZmluZWQpe3RoaXMucHJvdk5hbWU9S0pVUi5jcnlwdG8uVXRpbC5ERUZBVUxUUFJPVklERVJbdGhpcy5hbGdOYW1lXX10aGlzLnNldEFsZ0FuZFByb3ZpZGVyKHRoaXMuYWxnTmFtZSx0aGlzLnByb3ZOYW1lKX19fTtLSlVSLmNyeXB0by5NZXNzYWdlRGlnZXN0LmdldENhbm9uaWNhbEFsZ05hbWU9ZnVuY3Rpb24oYSl7aWYodHlwZW9mIGE9PT1cInN0cmluZ1wiKXthPWEudG9Mb3dlckNhc2UoKTthPWEucmVwbGFjZSgvLS8sXCJcIil9cmV0dXJuIGF9O0tKVVIuY3J5cHRvLk1lc3NhZ2VEaWdlc3QuZ2V0SGFzaExlbmd0aD1mdW5jdGlvbihjKXt2YXIgYj1LSlVSLmNyeXB0by5NZXNzYWdlRGlnZXN0O3ZhciBhPWIuZ2V0Q2Fub25pY2FsQWxnTmFtZShjKTtpZihiLkhBU0hMRU5HVEhbYV09PT11bmRlZmluZWQpe3Rocm93XCJub3Qgc3VwcG9ydGVkIGFsZ29yaXRobTogXCIrY31yZXR1cm4gYi5IQVNITEVOR1RIW2FdfTtLSlVSLmNyeXB0by5NZXNzYWdlRGlnZXN0LkhBU0hMRU5HVEg9e21kNToxNixzaGExOjIwLHNoYTIyNDoyOCxzaGEyNTY6MzIsc2hhMzg0OjQ4LHNoYTUxMjo2NCxyaXBlbWQxNjA6MjB9O0tKVVIuY3J5cHRvLk1hYz1mdW5jdGlvbihkKXt2YXIgZj1udWxsO3ZhciBjPW51bGw7dmFyIGE9bnVsbDt2YXIgZT1udWxsO3ZhciBiPW51bGw7dGhpcy5zZXRBbGdBbmRQcm92aWRlcj1mdW5jdGlvbihrLGkpe2s9ay50b0xvd2VyQ2FzZSgpO2lmKGs9PW51bGwpe2s9XCJobWFjc2hhMVwifWs9ay50b0xvd2VyQ2FzZSgpO2lmKGsuc3Vic3RyKDAsNCkhPVwiaG1hY1wiKXt0aHJvd1wic2V0QWxnQW5kUHJvdmlkZXIgdW5zdXBwb3J0ZWQgSE1BQyBhbGc6IFwiK2t9aWYoaT09PXVuZGVmaW5lZCl7aT1LSlVSLmNyeXB0by5VdGlsLkRFRkFVTFRQUk9WSURFUltrXX10aGlzLmFsZ1Byb3Y9aytcIi9cIitpO3ZhciBnPWsuc3Vic3RyKDQpO2lmKFwiOm1kNTpzaGExOnNoYTIyNDpzaGEyNTY6c2hhMzg0OnNoYTUxMjpyaXBlbWQxNjA6XCIuaW5kZXhPZihnKSE9LTEmJmk9PVwiY3J5cHRvanNcIil7dHJ5e3ZhciBqPUtKVVIuY3J5cHRvLlV0aWwuQ1JZUFRPSlNNRVNTQUdFRElHRVNUTkFNRVtnXTt0aGlzLm1hYz1DcnlwdG9KUy5hbGdvLkhNQUMuY3JlYXRlKGosdGhpcy5wYXNzKX1jYXRjaChoKXt0aHJvd1wic2V0QWxnQW5kUHJvdmlkZXIgaGFzaCBhbGcgc2V0IGZhaWwgaGFzaEFsZz1cIitnK1wiL1wiK2h9dGhpcy51cGRhdGVTdHJpbmc9ZnVuY3Rpb24obCl7dGhpcy5tYWMudXBkYXRlKGwpfTt0aGlzLnVwZGF0ZUhleD1mdW5jdGlvbihsKXt2YXIgbT1DcnlwdG9KUy5lbmMuSGV4LnBhcnNlKGwpO3RoaXMubWFjLnVwZGF0ZShtKX07dGhpcy5kb0ZpbmFsPWZ1bmN0aW9uKCl7dmFyIGw9dGhpcy5tYWMuZmluYWxpemUoKTtyZXR1cm4gbC50b1N0cmluZyhDcnlwdG9KUy5lbmMuSGV4KX07dGhpcy5kb0ZpbmFsU3RyaW5nPWZ1bmN0aW9uKGwpe3RoaXMudXBkYXRlU3RyaW5nKGwpO3JldHVybiB0aGlzLmRvRmluYWwoKX07dGhpcy5kb0ZpbmFsSGV4PWZ1bmN0aW9uKGwpe3RoaXMudXBkYXRlSGV4KGwpO3JldHVybiB0aGlzLmRvRmluYWwoKX19fTt0aGlzLnVwZGF0ZVN0cmluZz1mdW5jdGlvbihnKXt0aHJvd1widXBkYXRlU3RyaW5nKHN0cikgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGcvcHJvdjogXCIrdGhpcy5hbGdQcm92fTt0aGlzLnVwZGF0ZUhleD1mdW5jdGlvbihnKXt0aHJvd1widXBkYXRlSGV4KGhleCkgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGcvcHJvdjogXCIrdGhpcy5hbGdQcm92fTt0aGlzLmRvRmluYWw9ZnVuY3Rpb24oKXt0aHJvd1wiZGlnZXN0KCkgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGcvcHJvdjogXCIrdGhpcy5hbGdQcm92fTt0aGlzLmRvRmluYWxTdHJpbmc9ZnVuY3Rpb24oZyl7dGhyb3dcImRpZ2VzdFN0cmluZyhzdHIpIG5vdCBzdXBwb3J0ZWQgZm9yIHRoaXMgYWxnL3Byb3Y6IFwiK3RoaXMuYWxnUHJvdn07dGhpcy5kb0ZpbmFsSGV4PWZ1bmN0aW9uKGcpe3Rocm93XCJkaWdlc3RIZXgoaGV4KSBub3Qgc3VwcG9ydGVkIGZvciB0aGlzIGFsZy9wcm92OiBcIit0aGlzLmFsZ1Byb3Z9O3RoaXMuc2V0UGFzc3dvcmQ9ZnVuY3Rpb24oaCl7aWYodHlwZW9mIGg9PVwic3RyaW5nXCIpe3ZhciBnPWg7aWYoaC5sZW5ndGglMj09MXx8IWgubWF0Y2goL15bMC05QS1GYS1mXSskLykpe2c9cnN0cnRvaGV4KGgpfXRoaXMucGFzcz1DcnlwdG9KUy5lbmMuSGV4LnBhcnNlKGcpO3JldHVybn1pZih0eXBlb2YgaCE9XCJvYmplY3RcIil7dGhyb3dcIktKVVIuY3J5cHRvLk1hYyB1bnN1cHBvcnRlZCBwYXNzd29yZCB0eXBlOiBcIitofXZhciBnPW51bGw7aWYoaC5oZXghPT11bmRlZmluZWQpe2lmKGguaGV4Lmxlbmd0aCUyIT0wfHwhaC5oZXgubWF0Y2goL15bMC05QS1GYS1mXSskLykpe3Rocm93XCJNYWM6IHdyb25nIGhleCBwYXNzd29yZDogXCIraC5oZXh9Zz1oLmhleH1pZihoLnV0ZjghPT11bmRlZmluZWQpe2c9dXRmOHRvaGV4KGgudXRmOCl9aWYoaC5yc3RyIT09dW5kZWZpbmVkKXtnPXJzdHJ0b2hleChoLnJzdHIpfWlmKGguYjY0IT09dW5kZWZpbmVkKXtnPWI2NHRvaGV4KGguYjY0KX1pZihoLmI2NHUhPT11bmRlZmluZWQpe2c9YjY0dXRvaGV4KGguYjY0dSl9aWYoZz09bnVsbCl7dGhyb3dcIktKVVIuY3J5cHRvLk1hYyB1bnN1cHBvcnRlZCBwYXNzd29yZCB0eXBlOiBcIitofXRoaXMucGFzcz1DcnlwdG9KUy5lbmMuSGV4LnBhcnNlKGcpfTtpZihkIT09dW5kZWZpbmVkKXtpZihkLnBhc3MhPT11bmRlZmluZWQpe3RoaXMuc2V0UGFzc3dvcmQoZC5wYXNzKX1pZihkLmFsZyE9PXVuZGVmaW5lZCl7dGhpcy5hbGdOYW1lPWQuYWxnO2lmKGQucHJvdj09PXVuZGVmaW5lZCl7dGhpcy5wcm92TmFtZT1LSlVSLmNyeXB0by5VdGlsLkRFRkFVTFRQUk9WSURFUlt0aGlzLmFsZ05hbWVdfXRoaXMuc2V0QWxnQW5kUHJvdmlkZXIodGhpcy5hbGdOYW1lLHRoaXMucHJvdk5hbWUpfX19O0tKVVIuY3J5cHRvLlNpZ25hdHVyZT1mdW5jdGlvbihvKXt2YXIgcT1udWxsO3ZhciBuPW51bGw7dmFyIHI9bnVsbDt2YXIgYz1udWxsO3ZhciBsPW51bGw7dmFyIGQ9bnVsbDt2YXIgaz1udWxsO3ZhciBoPW51bGw7dmFyIHA9bnVsbDt2YXIgZT1udWxsO3ZhciBiPS0xO3ZhciBnPW51bGw7dmFyIGo9bnVsbDt2YXIgYT1udWxsO3ZhciBpPW51bGw7dmFyIGY9bnVsbDt0aGlzLl9zZXRBbGdOYW1lcz1mdW5jdGlvbigpe3ZhciBzPXRoaXMuYWxnTmFtZS5tYXRjaCgvXiguKyl3aXRoKC4rKSQvKTtpZihzKXt0aGlzLm1kQWxnTmFtZT1zWzFdLnRvTG93ZXJDYXNlKCk7dGhpcy5wdWJrZXlBbGdOYW1lPXNbMl0udG9Mb3dlckNhc2UoKX19O3RoaXMuX3plcm9QYWRkaW5nT2ZTaWduYXR1cmU9ZnVuY3Rpb24oeCx3KXt2YXIgdj1cIlwiO3ZhciB0PXcvNC14Lmxlbmd0aDtmb3IodmFyIHU9MDt1PHQ7dSsrKXt2PXYrXCIwXCJ9cmV0dXJuIHYreH07dGhpcy5zZXRBbGdBbmRQcm92aWRlcj1mdW5jdGlvbih1LHQpe3RoaXMuX3NldEFsZ05hbWVzKCk7aWYodCE9XCJjcnlwdG9qcy9qc3JzYVwiKXt0aHJvd1wicHJvdmlkZXIgbm90IHN1cHBvcnRlZDogXCIrdH1pZihcIjptZDU6c2hhMTpzaGEyMjQ6c2hhMjU2OnNoYTM4NDpzaGE1MTI6cmlwZW1kMTYwOlwiLmluZGV4T2YodGhpcy5tZEFsZ05hbWUpIT0tMSl7dHJ5e3RoaXMubWQ9bmV3IEtKVVIuY3J5cHRvLk1lc3NhZ2VEaWdlc3Qoe2FsZzp0aGlzLm1kQWxnTmFtZX0pfWNhdGNoKHMpe3Rocm93XCJzZXRBbGdBbmRQcm92aWRlciBoYXNoIGFsZyBzZXQgZmFpbCBhbGc9XCIrdGhpcy5tZEFsZ05hbWUrXCIvXCIrc310aGlzLmluaXQ9ZnVuY3Rpb24odyx4KXt2YXIgeT1udWxsO3RyeXtpZih4PT09dW5kZWZpbmVkKXt5PUtFWVVUSUwuZ2V0S2V5KHcpfWVsc2V7eT1LRVlVVElMLmdldEtleSh3LHgpfX1jYXRjaCh2KXt0aHJvd1wiaW5pdCBmYWlsZWQ6XCIrdn1pZih5LmlzUHJpdmF0ZT09PXRydWUpe3RoaXMucHJ2S2V5PXk7dGhpcy5zdGF0ZT1cIlNJR05cIn1lbHNle2lmKHkuaXNQdWJsaWM9PT10cnVlKXt0aGlzLnB1YktleT15O3RoaXMuc3RhdGU9XCJWRVJJRllcIn1lbHNle3Rocm93XCJpbml0IGZhaWxlZC46XCIreX19fTt0aGlzLnVwZGF0ZVN0cmluZz1mdW5jdGlvbih2KXt0aGlzLm1kLnVwZGF0ZVN0cmluZyh2KX07dGhpcy51cGRhdGVIZXg9ZnVuY3Rpb24odil7dGhpcy5tZC51cGRhdGVIZXgodil9O3RoaXMuc2lnbj1mdW5jdGlvbigpe3RoaXMuc0hhc2hIZXg9dGhpcy5tZC5kaWdlc3QoKTtpZih0eXBlb2YgdGhpcy5lY3BydmhleCE9XCJ1bmRlZmluZWRcIiYmdHlwZW9mIHRoaXMuZWNjdXJ2ZW5hbWUhPVwidW5kZWZpbmVkXCIpe3ZhciB2PW5ldyBLSlVSLmNyeXB0by5FQ0RTQSh7Y3VydmU6dGhpcy5lY2N1cnZlbmFtZX0pO3RoaXMuaFNpZ249di5zaWduSGV4KHRoaXMuc0hhc2hIZXgsdGhpcy5lY3BydmhleCl9ZWxzZXtpZih0aGlzLnBydktleSBpbnN0YW5jZW9mIFJTQUtleSYmdGhpcy5wdWJrZXlBbGdOYW1lPT09XCJyc2FhbmRtZ2YxXCIpe3RoaXMuaFNpZ249dGhpcy5wcnZLZXkuc2lnbldpdGhNZXNzYWdlSGFzaFBTUyh0aGlzLnNIYXNoSGV4LHRoaXMubWRBbGdOYW1lLHRoaXMucHNzU2FsdExlbil9ZWxzZXtpZih0aGlzLnBydktleSBpbnN0YW5jZW9mIFJTQUtleSYmdGhpcy5wdWJrZXlBbGdOYW1lPT09XCJyc2FcIil7dGhpcy5oU2lnbj10aGlzLnBydktleS5zaWduV2l0aE1lc3NhZ2VIYXNoKHRoaXMuc0hhc2hIZXgsdGhpcy5tZEFsZ05hbWUpfWVsc2V7aWYodGhpcy5wcnZLZXkgaW5zdGFuY2VvZiBLSlVSLmNyeXB0by5FQ0RTQSl7dGhpcy5oU2lnbj10aGlzLnBydktleS5zaWduV2l0aE1lc3NhZ2VIYXNoKHRoaXMuc0hhc2hIZXgpfWVsc2V7aWYodGhpcy5wcnZLZXkgaW5zdGFuY2VvZiBLSlVSLmNyeXB0by5EU0Epe3RoaXMuaFNpZ249dGhpcy5wcnZLZXkuc2lnbldpdGhNZXNzYWdlSGFzaCh0aGlzLnNIYXNoSGV4KX1lbHNle3Rocm93XCJTaWduYXR1cmU6IHVuc3VwcG9ydGVkIHByaXZhdGUga2V5IGFsZzogXCIrdGhpcy5wdWJrZXlBbGdOYW1lfX19fX1yZXR1cm4gdGhpcy5oU2lnbn07dGhpcy5zaWduU3RyaW5nPWZ1bmN0aW9uKHYpe3RoaXMudXBkYXRlU3RyaW5nKHYpO3JldHVybiB0aGlzLnNpZ24oKX07dGhpcy5zaWduSGV4PWZ1bmN0aW9uKHYpe3RoaXMudXBkYXRlSGV4KHYpO3JldHVybiB0aGlzLnNpZ24oKX07dGhpcy52ZXJpZnk9ZnVuY3Rpb24odil7dGhpcy5zSGFzaEhleD10aGlzLm1kLmRpZ2VzdCgpO2lmKHR5cGVvZiB0aGlzLmVjcHViaGV4IT1cInVuZGVmaW5lZFwiJiZ0eXBlb2YgdGhpcy5lY2N1cnZlbmFtZSE9XCJ1bmRlZmluZWRcIil7dmFyIHc9bmV3IEtKVVIuY3J5cHRvLkVDRFNBKHtjdXJ2ZTp0aGlzLmVjY3VydmVuYW1lfSk7cmV0dXJuIHcudmVyaWZ5SGV4KHRoaXMuc0hhc2hIZXgsdix0aGlzLmVjcHViaGV4KX1lbHNle2lmKHRoaXMucHViS2V5IGluc3RhbmNlb2YgUlNBS2V5JiZ0aGlzLnB1YmtleUFsZ05hbWU9PT1cInJzYWFuZG1nZjFcIil7cmV0dXJuIHRoaXMucHViS2V5LnZlcmlmeVdpdGhNZXNzYWdlSGFzaFBTUyh0aGlzLnNIYXNoSGV4LHYsdGhpcy5tZEFsZ05hbWUsdGhpcy5wc3NTYWx0TGVuKX1lbHNle2lmKHRoaXMucHViS2V5IGluc3RhbmNlb2YgUlNBS2V5JiZ0aGlzLnB1YmtleUFsZ05hbWU9PT1cInJzYVwiKXtyZXR1cm4gdGhpcy5wdWJLZXkudmVyaWZ5V2l0aE1lc3NhZ2VIYXNoKHRoaXMuc0hhc2hIZXgsdil9ZWxzZXtpZihLSlVSLmNyeXB0by5FQ0RTQSE9PXVuZGVmaW5lZCYmdGhpcy5wdWJLZXkgaW5zdGFuY2VvZiBLSlVSLmNyeXB0by5FQ0RTQSl7cmV0dXJuIHRoaXMucHViS2V5LnZlcmlmeVdpdGhNZXNzYWdlSGFzaCh0aGlzLnNIYXNoSGV4LHYpfWVsc2V7aWYoS0pVUi5jcnlwdG8uRFNBIT09dW5kZWZpbmVkJiZ0aGlzLnB1YktleSBpbnN0YW5jZW9mIEtKVVIuY3J5cHRvLkRTQSl7cmV0dXJuIHRoaXMucHViS2V5LnZlcmlmeVdpdGhNZXNzYWdlSGFzaCh0aGlzLnNIYXNoSGV4LHYpfWVsc2V7dGhyb3dcIlNpZ25hdHVyZTogdW5zdXBwb3J0ZWQgcHVibGljIGtleSBhbGc6IFwiK3RoaXMucHVia2V5QWxnTmFtZX19fX19fX19O3RoaXMuaW5pdD1mdW5jdGlvbihzLHQpe3Rocm93XCJpbml0KGtleSwgcGFzcykgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGc6cHJvdj1cIit0aGlzLmFsZ1Byb3ZOYW1lfTt0aGlzLnVwZGF0ZVN0cmluZz1mdW5jdGlvbihzKXt0aHJvd1widXBkYXRlU3RyaW5nKHN0cikgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGc6cHJvdj1cIit0aGlzLmFsZ1Byb3ZOYW1lfTt0aGlzLnVwZGF0ZUhleD1mdW5jdGlvbihzKXt0aHJvd1widXBkYXRlSGV4KGhleCkgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGc6cHJvdj1cIit0aGlzLmFsZ1Byb3ZOYW1lfTt0aGlzLnNpZ249ZnVuY3Rpb24oKXt0aHJvd1wic2lnbigpIG5vdCBzdXBwb3J0ZWQgZm9yIHRoaXMgYWxnOnByb3Y9XCIrdGhpcy5hbGdQcm92TmFtZX07dGhpcy5zaWduU3RyaW5nPWZ1bmN0aW9uKHMpe3Rocm93XCJkaWdlc3RTdHJpbmcoc3RyKSBub3Qgc3VwcG9ydGVkIGZvciB0aGlzIGFsZzpwcm92PVwiK3RoaXMuYWxnUHJvdk5hbWV9O3RoaXMuc2lnbkhleD1mdW5jdGlvbihzKXt0aHJvd1wiZGlnZXN0SGV4KGhleCkgbm90IHN1cHBvcnRlZCBmb3IgdGhpcyBhbGc6cHJvdj1cIit0aGlzLmFsZ1Byb3ZOYW1lfTt0aGlzLnZlcmlmeT1mdW5jdGlvbihzKXt0aHJvd1widmVyaWZ5KGhTaWdWYWwpIG5vdCBzdXBwb3J0ZWQgZm9yIHRoaXMgYWxnOnByb3Y9XCIrdGhpcy5hbGdQcm92TmFtZX07dGhpcy5pbml0UGFyYW1zPW87aWYobyE9PXVuZGVmaW5lZCl7aWYoby5hbGchPT11bmRlZmluZWQpe3RoaXMuYWxnTmFtZT1vLmFsZztpZihvLnByb3Y9PT11bmRlZmluZWQpe3RoaXMucHJvdk5hbWU9S0pVUi5jcnlwdG8uVXRpbC5ERUZBVUxUUFJPVklERVJbdGhpcy5hbGdOYW1lXX1lbHNle3RoaXMucHJvdk5hbWU9by5wcm92fXRoaXMuYWxnUHJvdk5hbWU9dGhpcy5hbGdOYW1lK1wiOlwiK3RoaXMucHJvdk5hbWU7dGhpcy5zZXRBbGdBbmRQcm92aWRlcih0aGlzLmFsZ05hbWUsdGhpcy5wcm92TmFtZSk7dGhpcy5fc2V0QWxnTmFtZXMoKX1pZihvLnBzc3NhbHRsZW4hPT11bmRlZmluZWQpe3RoaXMucHNzU2FsdExlbj1vLnBzc3NhbHRsZW59aWYoby5wcnZrZXlwZW0hPT11bmRlZmluZWQpe2lmKG8ucHJ2a2V5cGFzIT09dW5kZWZpbmVkKXt0aHJvd1wiYm90aCBwcnZrZXlwZW0gYW5kIHBydmtleXBhcyBwYXJhbWV0ZXJzIG5vdCBzdXBwb3J0ZWRcIn1lbHNle3RyeXt2YXIgcT1LRVlVVElMLmdldEtleShvLnBydmtleXBlbSk7dGhpcy5pbml0KHEpfWNhdGNoKG0pe3Rocm93XCJmYXRhbCBlcnJvciB0byBsb2FkIHBlbSBwcml2YXRlIGtleTogXCIrbX19fX19O0tKVVIuY3J5cHRvLkNpcGhlcj1mdW5jdGlvbihhKXt9O0tKVVIuY3J5cHRvLkNpcGhlci5lbmNyeXB0PWZ1bmN0aW9uKGUsZixkKXtpZihmIGluc3RhbmNlb2YgUlNBS2V5JiZmLmlzUHVibGljKXt2YXIgYz1LSlVSLmNyeXB0by5DaXBoZXIuZ2V0QWxnQnlLZXlBbmROYW1lKGYsZCk7aWYoYz09PVwiUlNBXCIpe3JldHVybiBmLmVuY3J5cHQoZSl9aWYoYz09PVwiUlNBT0FFUFwiKXtyZXR1cm4gZi5lbmNyeXB0T0FFUChlLFwic2hhMVwiKX12YXIgYj1jLm1hdGNoKC9eUlNBT0FFUChcXGQrKSQvKTtpZihiIT09bnVsbCl7cmV0dXJuIGYuZW5jcnlwdE9BRVAoZSxcInNoYVwiK2JbMV0pfXRocm93XCJDaXBoZXIuZW5jcnlwdDogdW5zdXBwb3J0ZWQgYWxnb3JpdGhtIGZvciBSU0FLZXk6IFwiK2R9ZWxzZXt0aHJvd1wiQ2lwaGVyLmVuY3J5cHQ6IHVuc3VwcG9ydGVkIGtleSBvciBhbGdvcml0aG1cIn19O0tKVVIuY3J5cHRvLkNpcGhlci5kZWNyeXB0PWZ1bmN0aW9uKGUsZixkKXtpZihmIGluc3RhbmNlb2YgUlNBS2V5JiZmLmlzUHJpdmF0ZSl7dmFyIGM9S0pVUi5jcnlwdG8uQ2lwaGVyLmdldEFsZ0J5S2V5QW5kTmFtZShmLGQpO2lmKGM9PT1cIlJTQVwiKXtyZXR1cm4gZi5kZWNyeXB0KGUpfWlmKGM9PT1cIlJTQU9BRVBcIil7cmV0dXJuIGYuZGVjcnlwdE9BRVAoZSxcInNoYTFcIil9dmFyIGI9Yy5tYXRjaCgvXlJTQU9BRVAoXFxkKykkLyk7aWYoYiE9PW51bGwpe3JldHVybiBmLmRlY3J5cHRPQUVQKGUsXCJzaGFcIitiWzFdKX10aHJvd1wiQ2lwaGVyLmRlY3J5cHQ6IHVuc3VwcG9ydGVkIGFsZ29yaXRobSBmb3IgUlNBS2V5OiBcIitkfWVsc2V7dGhyb3dcIkNpcGhlci5kZWNyeXB0OiB1bnN1cHBvcnRlZCBrZXkgb3IgYWxnb3JpdGhtXCJ9fTtLSlVSLmNyeXB0by5DaXBoZXIuZ2V0QWxnQnlLZXlBbmROYW1lPWZ1bmN0aW9uKGIsYSl7aWYoYiBpbnN0YW5jZW9mIFJTQUtleSl7aWYoXCI6UlNBOlJTQU9BRVA6UlNBT0FFUDIyNDpSU0FPQUVQMjU2OlJTQU9BRVAzODQ6UlNBT0FFUDUxMjpcIi5pbmRleE9mKGEpIT0tMSl7cmV0dXJuIGF9aWYoYT09PW51bGx8fGE9PT11bmRlZmluZWQpe3JldHVyblwiUlNBXCJ9dGhyb3dcImdldEFsZ0J5S2V5QW5kTmFtZTogbm90IHN1cHBvcnRlZCBhbGdvcml0aG0gbmFtZSBmb3IgUlNBS2V5OiBcIithfXRocm93XCJnZXRBbGdCeUtleUFuZE5hbWU6IG5vdCBzdXBwb3J0ZWQgYWxnb3JpdGhtIG5hbWU6IFwiK2F9O0tKVVIuY3J5cHRvLk9JRD1uZXcgZnVuY3Rpb24oKXt0aGlzLm9pZGhleDJuYW1lPXtcIjJhODY0ODg2ZjcwZDAxMDEwMVwiOlwicnNhRW5jcnlwdGlvblwiLFwiMmE4NjQ4Y2UzZDAyMDFcIjpcImVjUHVibGljS2V5XCIsXCIyYTg2NDhjZTM4MDQwMVwiOlwiZHNhXCIsXCIyYTg2NDhjZTNkMDMwMTA3XCI6XCJzZWNwMjU2cjFcIixcIjJiODEwNDAwMWZcIjpcInNlY3AxOTJrMVwiLFwiMmI4MTA0MDAyMVwiOlwic2VjcDIyNHIxXCIsXCIyYjgxMDQwMDBhXCI6XCJzZWNwMjU2azFcIixcIjJiODEwNDAwMjNcIjpcInNlY3A1MjFyMVwiLFwiMmI4MTA0MDAyMlwiOlwic2VjcDM4NHIxXCIsXCIyYTg2NDhjZTM4MDQwM1wiOlwiU0hBMXdpdGhEU0FcIixcIjYwODY0ODAxNjUwMzA0MDMwMVwiOlwiU0hBMjI0d2l0aERTQVwiLFwiNjA4NjQ4MDE2NTAzMDQwMzAyXCI6XCJTSEEyNTZ3aXRoRFNBXCIsfX07XG5pZih0eXBlb2YgS0pVUj09XCJ1bmRlZmluZWRcInx8IUtKVVIpe0tKVVI9e319aWYodHlwZW9mIEtKVVIuY3J5cHRvPT1cInVuZGVmaW5lZFwifHwhS0pVUi5jcnlwdG8pe0tKVVIuY3J5cHRvPXt9fUtKVVIuY3J5cHRvLkVDRFNBPWZ1bmN0aW9uKGgpe3ZhciBlPVwic2VjcDI1NnIxXCI7dmFyIGc9bnVsbDt2YXIgYj1udWxsO3ZhciBmPW51bGw7dmFyIGE9bmV3IFNlY3VyZVJhbmRvbSgpO3ZhciBkPW51bGw7dGhpcy50eXBlPVwiRUNcIjt0aGlzLmlzUHJpdmF0ZT1mYWxzZTt0aGlzLmlzUHVibGljPWZhbHNlO2Z1bmN0aW9uIGMocyxvLHIsbil7dmFyIGo9TWF0aC5tYXgoby5iaXRMZW5ndGgoKSxuLmJpdExlbmd0aCgpKTt2YXIgdD1zLmFkZDJEKHIpO3ZhciBxPXMuY3VydmUuZ2V0SW5maW5pdHkoKTtmb3IodmFyIHA9ai0xO3A+PTA7LS1wKXtxPXEudHdpY2UyRCgpO3Euej1CaWdJbnRlZ2VyLk9ORTtpZihvLnRlc3RCaXQocCkpe2lmKG4udGVzdEJpdChwKSl7cT1xLmFkZDJEKHQpfWVsc2V7cT1xLmFkZDJEKHMpfX1lbHNle2lmKG4udGVzdEJpdChwKSl7cT1xLmFkZDJEKHIpfX19cmV0dXJuIHF9dGhpcy5nZXRCaWdSYW5kb209ZnVuY3Rpb24oaSl7cmV0dXJuIG5ldyBCaWdJbnRlZ2VyKGkuYml0TGVuZ3RoKCksYSkubW9kKGkuc3VidHJhY3QoQmlnSW50ZWdlci5PTkUpKS5hZGQoQmlnSW50ZWdlci5PTkUpfTt0aGlzLnNldE5hbWVkQ3VydmU9ZnVuY3Rpb24oaSl7dGhpcy5lY3BhcmFtcz1LSlVSLmNyeXB0by5FQ1BhcmFtZXRlckRCLmdldEJ5TmFtZShpKTt0aGlzLnBydktleUhleD1udWxsO3RoaXMucHViS2V5SGV4PW51bGw7dGhpcy5jdXJ2ZU5hbWU9aX07dGhpcy5zZXRQcml2YXRlS2V5SGV4PWZ1bmN0aW9uKGkpe3RoaXMuaXNQcml2YXRlPXRydWU7dGhpcy5wcnZLZXlIZXg9aX07dGhpcy5zZXRQdWJsaWNLZXlIZXg9ZnVuY3Rpb24oaSl7dGhpcy5pc1B1YmxpYz10cnVlO3RoaXMucHViS2V5SGV4PWl9O3RoaXMuZ2V0UHVibGljS2V5WFlIZXg9ZnVuY3Rpb24oKXt2YXIgaz10aGlzLnB1YktleUhleDtpZihrLnN1YnN0cigwLDIpIT09XCIwNFwiKXt0aHJvd1widGhpcyBtZXRob2Qgc3VwcG9ydHMgdW5jb21wcmVzc2VkIGZvcm1hdCgwNCkgb25seVwifXZhciBqPXRoaXMuZWNwYXJhbXMua2V5bGVuLzQ7aWYoay5sZW5ndGghPT0yK2oqMil7dGhyb3dcIm1hbGZvcm1lZCBwdWJsaWMga2V5IGhleCBsZW5ndGhcIn12YXIgaT17fTtpLng9ay5zdWJzdHIoMixqKTtpLnk9ay5zdWJzdHIoMitqKTtyZXR1cm4gaX07dGhpcy5nZXRTaG9ydE5JU1RQQ3VydmVOYW1lPWZ1bmN0aW9uKCl7dmFyIGk9dGhpcy5jdXJ2ZU5hbWU7aWYoaT09PVwic2VjcDI1NnIxXCJ8fGk9PT1cIk5JU1QgUC0yNTZcInx8aT09PVwiUC0yNTZcInx8aT09PVwicHJpbWUyNTZ2MVwiKXtyZXR1cm5cIlAtMjU2XCJ9aWYoaT09PVwic2VjcDM4NHIxXCJ8fGk9PT1cIk5JU1QgUC0zODRcInx8aT09PVwiUC0zODRcIil7cmV0dXJuXCJQLTM4NFwifXJldHVybiBudWxsfTt0aGlzLmdlbmVyYXRlS2V5UGFpckhleD1mdW5jdGlvbigpe3ZhciBrPXRoaXMuZWNwYXJhbXMubjt2YXIgbj10aGlzLmdldEJpZ1JhbmRvbShrKTt2YXIgbD10aGlzLmVjcGFyYW1zLkcubXVsdGlwbHkobik7dmFyIHE9bC5nZXRYKCkudG9CaWdJbnRlZ2VyKCk7dmFyIG89bC5nZXRZKCkudG9CaWdJbnRlZ2VyKCk7dmFyIGk9dGhpcy5lY3BhcmFtcy5rZXlsZW4vNDt2YXIgbT0oXCIwMDAwMDAwMDAwXCIrbi50b1N0cmluZygxNikpLnNsaWNlKC1pKTt2YXIgcj0oXCIwMDAwMDAwMDAwXCIrcS50b1N0cmluZygxNikpLnNsaWNlKC1pKTt2YXIgcD0oXCIwMDAwMDAwMDAwXCIrby50b1N0cmluZygxNikpLnNsaWNlKC1pKTt2YXIgaj1cIjA0XCIrcitwO3RoaXMuc2V0UHJpdmF0ZUtleUhleChtKTt0aGlzLnNldFB1YmxpY0tleUhleChqKTtyZXR1cm57ZWNwcnZoZXg6bSxlY3B1YmhleDpqfX07dGhpcy5zaWduV2l0aE1lc3NhZ2VIYXNoPWZ1bmN0aW9uKGkpe3JldHVybiB0aGlzLnNpZ25IZXgoaSx0aGlzLnBydktleUhleCl9O3RoaXMuc2lnbkhleD1mdW5jdGlvbihvLGope3ZhciB0PW5ldyBCaWdJbnRlZ2VyKGosMTYpO3ZhciBsPXRoaXMuZWNwYXJhbXMubjt2YXIgcT1uZXcgQmlnSW50ZWdlcihvLDE2KTtkb3t2YXIgbT10aGlzLmdldEJpZ1JhbmRvbShsKTt2YXIgdT10aGlzLmVjcGFyYW1zLkc7dmFyIHA9dS5tdWx0aXBseShtKTt2YXIgaT1wLmdldFgoKS50b0JpZ0ludGVnZXIoKS5tb2QobCl9d2hpbGUoaS5jb21wYXJlVG8oQmlnSW50ZWdlci5aRVJPKTw9MCk7dmFyIHY9bS5tb2RJbnZlcnNlKGwpLm11bHRpcGx5KHEuYWRkKHQubXVsdGlwbHkoaSkpKS5tb2QobCk7cmV0dXJuIEtKVVIuY3J5cHRvLkVDRFNBLmJpUlNTaWdUb0FTTjFTaWcoaSx2KX07dGhpcy5zaWduPWZ1bmN0aW9uKG0sdSl7dmFyIHE9dTt2YXIgaj10aGlzLmVjcGFyYW1zLm47dmFyIHA9QmlnSW50ZWdlci5mcm9tQnl0ZUFycmF5VW5zaWduZWQobSk7ZG97dmFyIGw9dGhpcy5nZXRCaWdSYW5kb20oaik7dmFyIHQ9dGhpcy5lY3BhcmFtcy5HO3ZhciBvPXQubXVsdGlwbHkobCk7dmFyIGk9by5nZXRYKCkudG9CaWdJbnRlZ2VyKCkubW9kKGopfXdoaWxlKGkuY29tcGFyZVRvKEJpZ0ludGVnZXIuWkVSTyk8PTApO3ZhciB2PWwubW9kSW52ZXJzZShqKS5tdWx0aXBseShwLmFkZChxLm11bHRpcGx5KGkpKSkubW9kKGopO3JldHVybiB0aGlzLnNlcmlhbGl6ZVNpZyhpLHYpfTt0aGlzLnZlcmlmeVdpdGhNZXNzYWdlSGFzaD1mdW5jdGlvbihqLGkpe3JldHVybiB0aGlzLnZlcmlmeUhleChqLGksdGhpcy5wdWJLZXlIZXgpfTt0aGlzLnZlcmlmeUhleD1mdW5jdGlvbihtLGkscCl7dmFyIGwsajt2YXIgbz1LSlVSLmNyeXB0by5FQ0RTQS5wYXJzZVNpZ0hleChpKTtsPW8ucjtqPW8uczt2YXIgaztrPUVDUG9pbnRGcC5kZWNvZGVGcm9tSGV4KHRoaXMuZWNwYXJhbXMuY3VydmUscCk7dmFyIG49bmV3IEJpZ0ludGVnZXIobSwxNik7cmV0dXJuIHRoaXMudmVyaWZ5UmF3KG4sbCxqLGspfTt0aGlzLnZlcmlmeT1mdW5jdGlvbihvLHAsail7dmFyIGwsaTtpZihCaXRjb2luLlV0aWwuaXNBcnJheShwKSl7dmFyIG49dGhpcy5wYXJzZVNpZyhwKTtsPW4ucjtpPW4uc31lbHNle2lmKFwib2JqZWN0XCI9PT10eXBlb2YgcCYmcC5yJiZwLnMpe2w9cC5yO2k9cC5zfWVsc2V7dGhyb3dcIkludmFsaWQgdmFsdWUgZm9yIHNpZ25hdHVyZVwifX12YXIgaztpZihqIGluc3RhbmNlb2YgRUNQb2ludEZwKXtrPWp9ZWxzZXtpZihCaXRjb2luLlV0aWwuaXNBcnJheShqKSl7az1FQ1BvaW50RnAuZGVjb2RlRnJvbSh0aGlzLmVjcGFyYW1zLmN1cnZlLGopfWVsc2V7dGhyb3dcIkludmFsaWQgZm9ybWF0IGZvciBwdWJrZXkgdmFsdWUsIG11c3QgYmUgYnl0ZSBhcnJheSBvciBFQ1BvaW50RnBcIn19dmFyIG09QmlnSW50ZWdlci5mcm9tQnl0ZUFycmF5VW5zaWduZWQobyk7cmV0dXJuIHRoaXMudmVyaWZ5UmF3KG0sbCxpLGspfTt0aGlzLnZlcmlmeVJhdz1mdW5jdGlvbihvLGksdyxtKXt2YXIgbD10aGlzLmVjcGFyYW1zLm47dmFyIHU9dGhpcy5lY3BhcmFtcy5HO2lmKGkuY29tcGFyZVRvKEJpZ0ludGVnZXIuT05FKTwwfHxpLmNvbXBhcmVUbyhsKT49MCl7cmV0dXJuIGZhbHNlfWlmKHcuY29tcGFyZVRvKEJpZ0ludGVnZXIuT05FKTwwfHx3LmNvbXBhcmVUbyhsKT49MCl7cmV0dXJuIGZhbHNlfXZhciBwPXcubW9kSW52ZXJzZShsKTt2YXIgaz1vLm11bHRpcGx5KHApLm1vZChsKTt2YXIgaj1pLm11bHRpcGx5KHApLm1vZChsKTt2YXIgcT11Lm11bHRpcGx5KGspLmFkZChtLm11bHRpcGx5KGopKTt2YXIgdD1xLmdldFgoKS50b0JpZ0ludGVnZXIoKS5tb2QobCk7cmV0dXJuIHQuZXF1YWxzKGkpfTt0aGlzLnNlcmlhbGl6ZVNpZz1mdW5jdGlvbihrLGope3ZhciBsPWsudG9CeXRlQXJyYXlTaWduZWQoKTt2YXIgaT1qLnRvQnl0ZUFycmF5U2lnbmVkKCk7dmFyIG09W107bS5wdXNoKDIpO20ucHVzaChsLmxlbmd0aCk7bT1tLmNvbmNhdChsKTttLnB1c2goMik7bS5wdXNoKGkubGVuZ3RoKTttPW0uY29uY2F0KGkpO20udW5zaGlmdChtLmxlbmd0aCk7bS51bnNoaWZ0KDQ4KTtyZXR1cm4gbX07dGhpcy5wYXJzZVNpZz1mdW5jdGlvbihuKXt2YXIgbTtpZihuWzBdIT00OCl7dGhyb3cgbmV3IEVycm9yKFwiU2lnbmF0dXJlIG5vdCBhIHZhbGlkIERFUlNlcXVlbmNlXCIpfW09MjtpZihuW21dIT0yKXt0aHJvdyBuZXcgRXJyb3IoXCJGaXJzdCBlbGVtZW50IGluIHNpZ25hdHVyZSBtdXN0IGJlIGEgREVSSW50ZWdlclwiKX12YXIgbD1uLnNsaWNlKG0rMixtKzIrblttKzFdKTttKz0yK25bbSsxXTtpZihuW21dIT0yKXt0aHJvdyBuZXcgRXJyb3IoXCJTZWNvbmQgZWxlbWVudCBpbiBzaWduYXR1cmUgbXVzdCBiZSBhIERFUkludGVnZXJcIil9dmFyIGk9bi5zbGljZShtKzIsbSsyK25bbSsxXSk7bSs9MituW20rMV07dmFyIGs9QmlnSW50ZWdlci5mcm9tQnl0ZUFycmF5VW5zaWduZWQobCk7dmFyIGo9QmlnSW50ZWdlci5mcm9tQnl0ZUFycmF5VW5zaWduZWQoaSk7cmV0dXJue3I6ayxzOmp9fTt0aGlzLnBhcnNlU2lnQ29tcGFjdD1mdW5jdGlvbihtKXtpZihtLmxlbmd0aCE9PTY1KXt0aHJvd1wiU2lnbmF0dXJlIGhhcyB0aGUgd3JvbmcgbGVuZ3RoXCJ9dmFyIGo9bVswXS0yNztpZihqPDB8fGo+Nyl7dGhyb3dcIkludmFsaWQgc2lnbmF0dXJlIHR5cGVcIn12YXIgbz10aGlzLmVjcGFyYW1zLm47dmFyIGw9QmlnSW50ZWdlci5mcm9tQnl0ZUFycmF5VW5zaWduZWQobS5zbGljZSgxLDMzKSkubW9kKG8pO3ZhciBrPUJpZ0ludGVnZXIuZnJvbUJ5dGVBcnJheVVuc2lnbmVkKG0uc2xpY2UoMzMsNjUpKS5tb2Qobyk7cmV0dXJue3I6bCxzOmssaTpqfX07dGhpcy5yZWFkUEtDUzVQcnZLZXlIZXg9ZnVuY3Rpb24obCl7dmFyIG49QVNOMUhFWDt2YXIgbT1LSlVSLmNyeXB0by5FQ0RTQS5nZXROYW1lO3ZhciBwPW4uZ2V0VmJ5TGlzdDtpZihuLmlzQVNOMUhFWChsKT09PWZhbHNlKXt0aHJvd1wibm90IEFTTi4xIGhleCBzdHJpbmdcIn12YXIgaSxrLG87dHJ5e2k9cChsLDAsWzIsMF0sXCIwNlwiKTtrPXAobCwwLFsxXSxcIjA0XCIpO3RyeXtvPXAobCwwLFszLDBdLFwiMDNcIikuc3Vic3RyKDIpfWNhdGNoKGope319Y2F0Y2goail7dGhyb3dcIm1hbGZvcm1lZCBQS0NTIzEvNSBwbGFpbiBFQ0MgcHJpdmF0ZSBrZXlcIn10aGlzLmN1cnZlTmFtZT1tKGkpO2lmKHRoaXMuY3VydmVOYW1lPT09dW5kZWZpbmVkKXt0aHJvd1widW5zdXBwb3J0ZWQgY3VydmUgbmFtZVwifXRoaXMuc2V0TmFtZWRDdXJ2ZSh0aGlzLmN1cnZlTmFtZSk7dGhpcy5zZXRQdWJsaWNLZXlIZXgobyk7dGhpcy5zZXRQcml2YXRlS2V5SGV4KGspO3RoaXMuaXNQdWJsaWM9ZmFsc2V9O3RoaXMucmVhZFBLQ1M4UHJ2S2V5SGV4PWZ1bmN0aW9uKGwpe3ZhciBxPUFTTjFIRVg7dmFyIGk9S0pVUi5jcnlwdG8uRUNEU0EuZ2V0TmFtZTt2YXIgbj1xLmdldFZieUxpc3Q7aWYocS5pc0FTTjFIRVgobCk9PT1mYWxzZSl7dGhyb3dcIm5vdCBBU04uMSBoZXggc3RyaW5nXCJ9dmFyIGoscCxtLGs7dHJ5e2o9bihsLDAsWzEsMF0sXCIwNlwiKTtwPW4obCwwLFsxLDFdLFwiMDZcIik7bT1uKGwsMCxbMiwwLDFdLFwiMDRcIik7dHJ5e2s9bihsLDAsWzIsMCwyLDBdLFwiMDNcIikuc3Vic3RyKDIpfWNhdGNoKG8pe319Y2F0Y2gobyl7dGhyb3dcIm1hbGZvcm1lZCBQS0NTIzggcGxhaW4gRUNDIHByaXZhdGUga2V5XCJ9dGhpcy5jdXJ2ZU5hbWU9aShwKTtpZih0aGlzLmN1cnZlTmFtZT09PXVuZGVmaW5lZCl7dGhyb3dcInVuc3VwcG9ydGVkIGN1cnZlIG5hbWVcIn10aGlzLnNldE5hbWVkQ3VydmUodGhpcy5jdXJ2ZU5hbWUpO3RoaXMuc2V0UHVibGljS2V5SGV4KGspO3RoaXMuc2V0UHJpdmF0ZUtleUhleChtKTt0aGlzLmlzUHVibGljPWZhbHNlfTt0aGlzLnJlYWRQS0NTOFB1YktleUhleD1mdW5jdGlvbihsKXt2YXIgbj1BU04xSEVYO3ZhciBtPUtKVVIuY3J5cHRvLkVDRFNBLmdldE5hbWU7dmFyIHA9bi5nZXRWYnlMaXN0O2lmKG4uaXNBU04xSEVYKGwpPT09ZmFsc2Upe3Rocm93XCJub3QgQVNOLjEgaGV4IHN0cmluZ1wifXZhciBrLGksbzt0cnl7az1wKGwsMCxbMCwwXSxcIjA2XCIpO2k9cChsLDAsWzAsMV0sXCIwNlwiKTtvPXAobCwwLFsxXSxcIjAzXCIpLnN1YnN0cigyKX1jYXRjaChqKXt0aHJvd1wibWFsZm9ybWVkIFBLQ1MjOCBFQ0MgcHVibGljIGtleVwifXRoaXMuY3VydmVOYW1lPW0oaSk7aWYodGhpcy5jdXJ2ZU5hbWU9PT1udWxsKXt0aHJvd1widW5zdXBwb3J0ZWQgY3VydmUgbmFtZVwifXRoaXMuc2V0TmFtZWRDdXJ2ZSh0aGlzLmN1cnZlTmFtZSk7dGhpcy5zZXRQdWJsaWNLZXlIZXgobyl9O3RoaXMucmVhZENlcnRQdWJLZXlIZXg9ZnVuY3Rpb24oayxwKXtpZihwIT09NSl7cD02fXZhciBtPUFTTjFIRVg7dmFyIGw9S0pVUi5jcnlwdG8uRUNEU0EuZ2V0TmFtZTt2YXIgbz1tLmdldFZieUxpc3Q7aWYobS5pc0FTTjFIRVgoayk9PT1mYWxzZSl7dGhyb3dcIm5vdCBBU04uMSBoZXggc3RyaW5nXCJ9dmFyIGksbjt0cnl7aT1vKGssMCxbMCxwLDAsMV0sXCIwNlwiKTtuPW8oaywwLFswLHAsMV0sXCIwM1wiKS5zdWJzdHIoMil9Y2F0Y2goail7dGhyb3dcIm1hbGZvcm1lZCBYLjUwOSBjZXJ0aWZpY2F0ZSBFQ0MgcHVibGljIGtleVwifXRoaXMuY3VydmVOYW1lPWwoaSk7aWYodGhpcy5jdXJ2ZU5hbWU9PT1udWxsKXt0aHJvd1widW5zdXBwb3J0ZWQgY3VydmUgbmFtZVwifXRoaXMuc2V0TmFtZWRDdXJ2ZSh0aGlzLmN1cnZlTmFtZSk7dGhpcy5zZXRQdWJsaWNLZXlIZXgobil9O2lmKGghPT11bmRlZmluZWQpe2lmKGguY3VydmUhPT11bmRlZmluZWQpe3RoaXMuY3VydmVOYW1lPWguY3VydmV9fWlmKHRoaXMuY3VydmVOYW1lPT09dW5kZWZpbmVkKXt0aGlzLmN1cnZlTmFtZT1lfXRoaXMuc2V0TmFtZWRDdXJ2ZSh0aGlzLmN1cnZlTmFtZSk7aWYoaCE9PXVuZGVmaW5lZCl7aWYoaC5wcnYhPT11bmRlZmluZWQpe3RoaXMuc2V0UHJpdmF0ZUtleUhleChoLnBydil9aWYoaC5wdWIhPT11bmRlZmluZWQpe3RoaXMuc2V0UHVibGljS2V5SGV4KGgucHViKX19fTtLSlVSLmNyeXB0by5FQ0RTQS5wYXJzZVNpZ0hleD1mdW5jdGlvbihhKXt2YXIgYj1LSlVSLmNyeXB0by5FQ0RTQS5wYXJzZVNpZ0hleEluSGV4UlMoYSk7dmFyIGQ9bmV3IEJpZ0ludGVnZXIoYi5yLDE2KTt2YXIgYz1uZXcgQmlnSW50ZWdlcihiLnMsMTYpO3JldHVybntyOmQsczpjfX07S0pVUi5jcnlwdG8uRUNEU0EucGFyc2VTaWdIZXhJbkhleFJTPWZ1bmN0aW9uKGYpe3ZhciBqPUFTTjFIRVg7dmFyIGk9ai5nZXRDaGlsZElkeDt2YXIgZz1qLmdldFY7aWYoZi5zdWJzdHIoMCwyKSE9XCIzMFwiKXt0aHJvd1wic2lnbmF0dXJlIGlzIG5vdCBhIEFTTi4xIHNlcXVlbmNlXCJ9dmFyIGg9aShmLDApO2lmKGgubGVuZ3RoIT0yKXt0aHJvd1wibnVtYmVyIG9mIHNpZ25hdHVyZSBBU04uMSBzZXF1ZW5jZSBlbGVtZW50cyBzZWVtIHdyb25nXCJ9dmFyIGU9aFswXTt2YXIgZD1oWzFdO2lmKGYuc3Vic3RyKGUsMikhPVwiMDJcIil7dGhyb3dcIjFzdCBpdGVtIG9mIHNlcXVlbmUgb2Ygc2lnbmF0dXJlIGlzIG5vdCBBU04uMSBpbnRlZ2VyXCJ9aWYoZi5zdWJzdHIoZCwyKSE9XCIwMlwiKXt0aHJvd1wiMm5kIGl0ZW0gb2Ygc2VxdWVuZSBvZiBzaWduYXR1cmUgaXMgbm90IEFTTi4xIGludGVnZXJcIn12YXIgYz1nKGYsZSk7dmFyIGI9ZyhmLGQpO3JldHVybntyOmMsczpifX07S0pVUi5jcnlwdG8uRUNEU0EuYXNuMVNpZ1RvQ29uY2F0U2lnPWZ1bmN0aW9uKGMpe3ZhciBkPUtKVVIuY3J5cHRvLkVDRFNBLnBhcnNlU2lnSGV4SW5IZXhSUyhjKTt2YXIgYj1kLnI7dmFyIGE9ZC5zO2lmKGIuc3Vic3RyKDAsMik9PVwiMDBcIiYmKGIubGVuZ3RoJTMyKT09Mil7Yj1iLnN1YnN0cigyKX1pZihhLnN1YnN0cigwLDIpPT1cIjAwXCImJihhLmxlbmd0aCUzMik9PTIpe2E9YS5zdWJzdHIoMil9aWYoKGIubGVuZ3RoJTMyKT09MzApe2I9XCIwMFwiK2J9aWYoKGEubGVuZ3RoJTMyKT09MzApe2E9XCIwMFwiK2F9aWYoYi5sZW5ndGglMzIhPTApe3Rocm93XCJ1bmtub3duIEVDRFNBIHNpZyByIGxlbmd0aCBlcnJvclwifWlmKGEubGVuZ3RoJTMyIT0wKXt0aHJvd1widW5rbm93biBFQ0RTQSBzaWcgcyBsZW5ndGggZXJyb3JcIn1yZXR1cm4gYithfTtLSlVSLmNyeXB0by5FQ0RTQS5jb25jYXRTaWdUb0FTTjFTaWc9ZnVuY3Rpb24oYSl7aWYoKCgoYS5sZW5ndGgvMikqOCklKDE2KjgpKSE9MCl7dGhyb3dcInVua25vd24gRUNEU0EgY29uY2F0aW5hdGVkIHItcyBzaWcgIGxlbmd0aCBlcnJvclwifXZhciBjPWEuc3Vic3RyKDAsYS5sZW5ndGgvMik7dmFyIGI9YS5zdWJzdHIoYS5sZW5ndGgvMik7cmV0dXJuIEtKVVIuY3J5cHRvLkVDRFNBLmhleFJTU2lnVG9BU04xU2lnKGMsYil9O0tKVVIuY3J5cHRvLkVDRFNBLmhleFJTU2lnVG9BU04xU2lnPWZ1bmN0aW9uKGIsYSl7dmFyIGQ9bmV3IEJpZ0ludGVnZXIoYiwxNik7dmFyIGM9bmV3IEJpZ0ludGVnZXIoYSwxNik7cmV0dXJuIEtKVVIuY3J5cHRvLkVDRFNBLmJpUlNTaWdUb0FTTjFTaWcoZCxjKX07S0pVUi5jcnlwdG8uRUNEU0EuYmlSU1NpZ1RvQVNOMVNpZz1mdW5jdGlvbihmLGQpe3ZhciBjPUtKVVIuYXNuMTt2YXIgYj1uZXcgYy5ERVJJbnRlZ2VyKHtiaWdpbnQ6Zn0pO3ZhciBhPW5ldyBjLkRFUkludGVnZXIoe2JpZ2ludDpkfSk7dmFyIGU9bmV3IGMuREVSU2VxdWVuY2Uoe2FycmF5OltiLGFdfSk7cmV0dXJuIGUuZ2V0RW5jb2RlZEhleCgpfTtLSlVSLmNyeXB0by5FQ0RTQS5nZXROYW1lPWZ1bmN0aW9uKGEpe2lmKGE9PT1cIjJhODY0OGNlM2QwMzAxMDdcIil7cmV0dXJuXCJzZWNwMjU2cjFcIn1pZihhPT09XCIyYjgxMDQwMDBhXCIpe3JldHVyblwic2VjcDI1NmsxXCJ9aWYoYT09PVwiMmI4MTA0MDAyMlwiKXtyZXR1cm5cInNlY3AzODRyMVwifWlmKFwifHNlY3AyNTZyMXxOSVNUIFAtMjU2fFAtMjU2fHByaW1lMjU2djF8XCIuaW5kZXhPZihhKSE9PS0xKXtyZXR1cm5cInNlY3AyNTZyMVwifWlmKFwifHNlY3AyNTZrMXxcIi5pbmRleE9mKGEpIT09LTEpe3JldHVyblwic2VjcDI1NmsxXCJ9aWYoXCJ8c2VjcDM4NHIxfE5JU1QgUC0zODR8UC0zODR8XCIuaW5kZXhPZihhKSE9PS0xKXtyZXR1cm5cInNlY3AzODRyMVwifXJldHVybiBudWxsfTtcbmlmKHR5cGVvZiBLSlVSPT1cInVuZGVmaW5lZFwifHwhS0pVUil7S0pVUj17fX1pZih0eXBlb2YgS0pVUi5jcnlwdG89PVwidW5kZWZpbmVkXCJ8fCFLSlVSLmNyeXB0byl7S0pVUi5jcnlwdG89e319S0pVUi5jcnlwdG8uRUNQYXJhbWV0ZXJEQj1uZXcgZnVuY3Rpb24oKXt2YXIgYj17fTt2YXIgYz17fTtmdW5jdGlvbiBhKGQpe3JldHVybiBuZXcgQmlnSW50ZWdlcihkLDE2KX10aGlzLmdldEJ5TmFtZT1mdW5jdGlvbihlKXt2YXIgZD1lO2lmKHR5cGVvZiBjW2RdIT1cInVuZGVmaW5lZFwiKXtkPWNbZV19aWYodHlwZW9mIGJbZF0hPVwidW5kZWZpbmVkXCIpe3JldHVybiBiW2RdfXRocm93XCJ1bnJlZ2lzdGVyZWQgRUMgY3VydmUgbmFtZTogXCIrZH07dGhpcy5yZWdpc3Q9ZnVuY3Rpb24oQSxsLG8sZyxtLGUsaixmLGssdSxkLHgpe2JbQV09e307dmFyIHM9YShvKTt2YXIgej1hKGcpO3ZhciB5PWEobSk7dmFyIHQ9YShlKTt2YXIgdz1hKGopO3ZhciByPW5ldyBFQ0N1cnZlRnAocyx6LHkpO3ZhciBxPXIuZGVjb2RlUG9pbnRIZXgoXCIwNFwiK2Yrayk7YltBXVtcIm5hbWVcIl09QTtiW0FdW1wia2V5bGVuXCJdPWw7YltBXVtcImN1cnZlXCJdPXI7YltBXVtcIkdcIl09cTtiW0FdW1wiblwiXT10O2JbQV1bXCJoXCJdPXc7YltBXVtcIm9pZFwiXT1kO2JbQV1bXCJpbmZvXCJdPXg7Zm9yKHZhciB2PTA7djx1Lmxlbmd0aDt2Kyspe2NbdVt2XV09QX19fTtLSlVSLmNyeXB0by5FQ1BhcmFtZXRlckRCLnJlZ2lzdChcInNlY3AxMjhyMVwiLDEyOCxcIkZGRkZGRkZERkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGXCIsXCJGRkZGRkZGREZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ1wiLFwiRTg3NTc5QzExMDc5RjQzREQ4MjQ5OTNDMkNFRTVFRDNcIixcIkZGRkZGRkZFMDAwMDAwMDA3NUEzMEQxQjkwMzhBMTE1XCIsXCIxXCIsXCIxNjFGRjc1MjhCODk5QjJEMEMyODYwN0NBNTJDNUI4NlwiLFwiQ0Y1QUM4Mzk1QkFGRUIxM0MwMkRBMjkyRERFRDdBODNcIixbXSxcIlwiLFwic2VjcDEyOHIxIDogU0VDRyBjdXJ2ZSBvdmVyIGEgMTI4IGJpdCBwcmltZSBmaWVsZFwiKTtLSlVSLmNyeXB0by5FQ1BhcmFtZXRlckRCLnJlZ2lzdChcInNlY3AxNjBrMVwiLDE2MCxcIkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZFRkZGRkFDNzNcIixcIjBcIixcIjdcIixcIjAxMDAwMDAwMDAwMDAwMDAwMDAwMDFCOEZBMTZERkFCOUFDQTE2QjZCM1wiLFwiMVwiLFwiM0I0QzM4MkNFMzdBQTE5MkE0MDE5RTc2MzAzNkY0RjVERDREN0VCQlwiLFwiOTM4Q0Y5MzUzMThGRENFRDZCQzI4Mjg2NTMxNzMzQzNGMDNDNEZFRVwiLFtdLFwiXCIsXCJzZWNwMTYwazEgOiBTRUNHIGN1cnZlIG92ZXIgYSAxNjAgYml0IHByaW1lIGZpZWxkXCIpO0tKVVIuY3J5cHRvLkVDUGFyYW1ldGVyREIucmVnaXN0KFwic2VjcDE2MHIxXCIsMTYwLFwiRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkY3RkZGRkZGRlwiLFwiRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkY3RkZGRkZGQ1wiLFwiMUM5N0JFRkM1NEJEN0E4QjY1QUNGODlGODFENEQ0QURDNTY1RkE0NVwiLFwiMDEwMDAwMDAwMDAwMDAwMDAwMDAwMUY0QzhGOTI3QUVEM0NBNzUyMjU3XCIsXCIxXCIsXCI0QTk2QjU2ODhFRjU3MzI4NDY2NDY5ODk2OEMzOEJCOTEzQ0JGQzgyXCIsXCIyM0E2Mjg1NTMxNjg5NDdENTlEQ0M5MTIwNDIzNTEzNzdBQzVGQjMyXCIsW10sXCJcIixcInNlY3AxNjByMSA6IFNFQ0cgY3VydmUgb3ZlciBhIDE2MCBiaXQgcHJpbWUgZmllbGRcIik7S0pVUi5jcnlwdG8uRUNQYXJhbWV0ZXJEQi5yZWdpc3QoXCJzZWNwMTkyazFcIiwxOTIsXCJGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZFRkZGRkVFMzdcIixcIjBcIixcIjNcIixcIkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRTI2RjJGQzE3MEY2OTQ2NkE3NERFRkQ4RFwiLFwiMVwiLFwiREI0RkYxMEVDMDU3RTlBRTI2QjA3RDAyODBCN0Y0MzQxREE1RDFCMUVBRTA2QzdEXCIsXCI5QjJGMkY2RDlDNTYyOEE3ODQ0MTYzRDAxNUJFODYzNDQwODJBQTg4RDk1RTJGOURcIixbXSk7S0pVUi5jcnlwdG8uRUNQYXJhbWV0ZXJEQi5yZWdpc3QoXCJzZWNwMTkycjFcIiwxOTIsXCJGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRUZGRkZGRkZGRkZGRkZGRkZcIixcIkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZFRkZGRkZGRkZGRkZGRkZGQ1wiLFwiNjQyMTA1MTlFNTlDODBFNzBGQTdFOUFCNzIyNDMwNDlGRUI4REVFQ0MxNDZCOUIxXCIsXCJGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkY5OURFRjgzNjE0NkJDOUIxQjREMjI4MzFcIixcIjFcIixcIjE4OERBODBFQjAzMDkwRjY3Q0JGMjBFQjQzQTE4ODAwRjRGRjBBRkQ4MkZGMTAxMlwiLFwiMDcxOTJCOTVGRkM4REE3ODYzMTAxMUVENkIyNENERDU3M0Y5NzdBMTFFNzk0ODExXCIsW10pO0tKVVIuY3J5cHRvLkVDUGFyYW1ldGVyREIucmVnaXN0KFwic2VjcDIyNHIxXCIsMjI0LFwiRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkYwMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDFcIixcIkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZFRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZFXCIsXCJCNDA1MEE4NTBDMDRCM0FCRjU0MTMyNTY1MDQ0QjBCN0Q3QkZEOEJBMjcwQjM5NDMyMzU1RkZCNFwiLFwiRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRjE2QTJFMEI4RjAzRTEzREQyOTQ1NUM1QzJBM0RcIixcIjFcIixcIkI3MEUwQ0JENkJCNEJGN0YzMjEzOTBCOTRBMDNDMUQzNTZDMjExMjIzNDMyODBENjExNUMxRDIxXCIsXCJCRDM3NjM4OEI1RjcyM0ZCNEMyMkRGRTZDRDQzNzVBMDVBMDc0NzY0NDRENTgxOTk4NTAwN0UzNFwiLFtdKTtLSlVSLmNyeXB0by5FQ1BhcmFtZXRlckRCLnJlZ2lzdChcInNlY3AyNTZrMVwiLDI1NixcIkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZFRkZGRkZDMkZcIixcIjBcIixcIjdcIixcIkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZFQkFBRURDRTZBRjQ4QTAzQkJGRDI1RThDRDAzNjQxNDFcIixcIjFcIixcIjc5QkU2NjdFRjlEQ0JCQUM1NUEwNjI5NUNFODcwQjA3MDI5QkZDREIyRENFMjhEOTU5RjI4MTVCMTZGODE3OThcIixcIjQ4M0FEQTc3MjZBM0M0NjU1REE0RkJGQzBFMTEwOEE4RkQxN0I0NDhBNjg1NTQxOTlDNDdEMDhGRkIxMEQ0QjhcIixbXSk7S0pVUi5jcnlwdG8uRUNQYXJhbWV0ZXJEQi5yZWdpc3QoXCJzZWNwMjU2cjFcIiwyNTYsXCJGRkZGRkZGRjAwMDAwMDAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGXCIsXCJGRkZGRkZGRjAwMDAwMDAxMDAwMDAwMDAwMDAwMDAwMDAwMDAwMDAwRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZDXCIsXCI1QUM2MzVEOEFBM0E5M0U3QjNFQkJENTU3Njk4ODZCQzY1MUQwNkIwQ0M1M0IwRjYzQkNFM0MzRTI3RDI2MDRCXCIsXCJGRkZGRkZGRjAwMDAwMDAwRkZGRkZGRkZGRkZGRkZGRkJDRTZGQUFEQTcxNzlFODRGM0I5Q0FDMkZDNjMyNTUxXCIsXCIxXCIsXCI2QjE3RDFGMkUxMkM0MjQ3RjhCQ0U2RTU2M0E0NDBGMjc3MDM3RDgxMkRFQjMzQTBGNEExMzk0NUQ4OThDMjk2XCIsXCI0RkUzNDJFMkZFMUE3RjlCOEVFN0VCNEE3QzBGOUUxNjJCQ0UzMzU3NkIzMTVFQ0VDQkI2NDA2ODM3QkY1MUY1XCIsW1wiTklTVCBQLTI1NlwiLFwiUC0yNTZcIixcInByaW1lMjU2djFcIl0pO0tKVVIuY3J5cHRvLkVDUGFyYW1ldGVyREIucmVnaXN0KFwic2VjcDM4NHIxXCIsMzg0LFwiRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRUZGRkZGRkZGMDAwMDAwMDAwMDAwMDAwMEZGRkZGRkZGXCIsXCJGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZFRkZGRkZGRkYwMDAwMDAwMDAwMDAwMDAwRkZGRkZGRkNcIixcIkIzMzEyRkE3RTIzRUU3RTQ5ODhFMDU2QkUzRjgyRDE5MTgxRDlDNkVGRTgxNDExMjAzMTQwODhGNTAxMzg3NUFDNjU2Mzk4RDhBMkVEMTlEMkE4NUM4RUREM0VDMkFFRlwiLFwiRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQzc2MzREODFGNDM3MkRERjU4MUEwREIyNDhCMEE3N0FFQ0VDMTk2QUNDQzUyOTczXCIsXCIxXCIsXCJBQTg3Q0EyMkJFOEIwNTM3OEVCMUM3MUVGMzIwQUQ3NDZFMUQzQjYyOEJBNzlCOTg1OUY3NDFFMDgyNTQyQTM4NTUwMkYyNURCRjU1Mjk2QzNBNTQ1RTM4NzI3NjBBQjdcIixcIjM2MTdkZTRhOTYyNjJjNmY1ZDllOThiZjkyOTJkYzI5ZjhmNDFkYmQyODlhMTQ3Y2U5ZGEzMTEzYjVmMGI4YzAwYTYwYjFjZTFkN2U4MTlkN2E0MzFkN2M5MGVhMGU1ZlwiLFtcIk5JU1QgUC0zODRcIixcIlAtMzg0XCJdKTtLSlVSLmNyeXB0by5FQ1BhcmFtZXRlckRCLnJlZ2lzdChcInNlY3A1MjFyMVwiLDUyMSxcIjFGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGXCIsXCIxRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGQ1wiLFwiMDUxOTUzRUI5NjE4RTFDOUExRjkyOUEyMUEwQjY4NTQwRUVBMkRBNzI1Qjk5QjMxNUYzQjhCNDg5OTE4RUYxMDlFMTU2MTkzOTUxRUM3RTkzN0IxNjUyQzBCRDNCQjFCRjA3MzU3M0RGODgzRDJDMzRGMUVGNDUxRkQ0NkI1MDNGMDBcIixcIjFGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkZGRkE1MTg2ODc4M0JGMkY5NjZCN0ZDQzAxNDhGNzA5QTVEMDNCQjVDOUI4ODk5QzQ3QUVCQjZGQjcxRTkxMzg2NDA5XCIsXCIxXCIsXCJDNjg1OEUwNkI3MDQwNEU5Q0Q5RTNFQ0I2NjIzOTVCNDQyOUM2NDgxMzkwNTNGQjUyMUY4MjhBRjYwNkI0RDNEQkFBMTRCNUU3N0VGRTc1OTI4RkUxREMxMjdBMkZGQThERTMzNDhCM0MxODU2QTQyOUJGOTdFN0UzMUMyRTVCRDY2XCIsXCIwMTE4MzkyOTZhNzg5YTNiYzAwNDVjOGE1ZmI0MmM3ZDFiZDk5OGY1NDQ0OTU3OWI0NDY4MTdhZmJkMTcyNzNlNjYyYzk3ZWU3Mjk5NWVmNDI2NDBjNTUwYjkwMTNmYWQwNzYxMzUzYzcwODZhMjcyYzI0MDg4YmU5NDc2OWZkMTY2NTBcIixbXCJOSVNUIFAtNTIxXCIsXCJQLTUyMVwiXSk7XG52YXIgS0VZVVRJTD1mdW5jdGlvbigpe3ZhciBkPWZ1bmN0aW9uKHAscixxKXtyZXR1cm4gayhDcnlwdG9KUy5BRVMscCxyLHEpfTt2YXIgZT1mdW5jdGlvbihwLHIscSl7cmV0dXJuIGsoQ3J5cHRvSlMuVHJpcGxlREVTLHAscixxKX07dmFyIGE9ZnVuY3Rpb24ocCxyLHEpe3JldHVybiBrKENyeXB0b0pTLkRFUyxwLHIscSl9O3ZhciBrPWZ1bmN0aW9uKHMseCx1LHEpe3ZhciByPUNyeXB0b0pTLmVuYy5IZXgucGFyc2UoeCk7dmFyIHc9Q3J5cHRvSlMuZW5jLkhleC5wYXJzZSh1KTt2YXIgcD1DcnlwdG9KUy5lbmMuSGV4LnBhcnNlKHEpO3ZhciB0PXt9O3Qua2V5PXc7dC5pdj1wO3QuY2lwaGVydGV4dD1yO3ZhciB2PXMuZGVjcnlwdCh0LHcse2l2OnB9KTtyZXR1cm4gQ3J5cHRvSlMuZW5jLkhleC5zdHJpbmdpZnkodil9O3ZhciBsPWZ1bmN0aW9uKHAscixxKXtyZXR1cm4gZyhDcnlwdG9KUy5BRVMscCxyLHEpfTt2YXIgbz1mdW5jdGlvbihwLHIscSl7cmV0dXJuIGcoQ3J5cHRvSlMuVHJpcGxlREVTLHAscixxKX07dmFyIGY9ZnVuY3Rpb24ocCxyLHEpe3JldHVybiBnKENyeXB0b0pTLkRFUyxwLHIscSl9O3ZhciBnPWZ1bmN0aW9uKHQseSx2LHEpe3ZhciBzPUNyeXB0b0pTLmVuYy5IZXgucGFyc2UoeSk7dmFyIHg9Q3J5cHRvSlMuZW5jLkhleC5wYXJzZSh2KTt2YXIgcD1DcnlwdG9KUy5lbmMuSGV4LnBhcnNlKHEpO3ZhciB3PXQuZW5jcnlwdChzLHgse2l2OnB9KTt2YXIgcj1DcnlwdG9KUy5lbmMuSGV4LnBhcnNlKHcudG9TdHJpbmcoKSk7dmFyIHU9Q3J5cHRvSlMuZW5jLkJhc2U2NC5zdHJpbmdpZnkocik7cmV0dXJuIHV9O3ZhciBpPXtcIkFFUy0yNTYtQ0JDXCI6e3Byb2M6ZCxlcHJvYzpsLGtleWxlbjozMixpdmxlbjoxNn0sXCJBRVMtMTkyLUNCQ1wiOntwcm9jOmQsZXByb2M6bCxrZXlsZW46MjQsaXZsZW46MTZ9LFwiQUVTLTEyOC1DQkNcIjp7cHJvYzpkLGVwcm9jOmwsa2V5bGVuOjE2LGl2bGVuOjE2fSxcIkRFUy1FREUzLUNCQ1wiOntwcm9jOmUsZXByb2M6byxrZXlsZW46MjQsaXZsZW46OH0sXCJERVMtQ0JDXCI6e3Byb2M6YSxlcHJvYzpmLGtleWxlbjo4LGl2bGVuOjh9fTt2YXIgYz1mdW5jdGlvbihwKXtyZXR1cm4gaVtwXVtcInByb2NcIl19O3ZhciBtPWZ1bmN0aW9uKHApe3ZhciByPUNyeXB0b0pTLmxpYi5Xb3JkQXJyYXkucmFuZG9tKHApO3ZhciBxPUNyeXB0b0pTLmVuYy5IZXguc3RyaW5naWZ5KHIpO3JldHVybiBxfTt2YXIgbj1mdW5jdGlvbih2KXt2YXIgdz17fTt2YXIgcT12Lm1hdGNoKG5ldyBSZWdFeHAoXCJERUstSW5mbzogKFteLF0rKSwoWzAtOUEtRmEtZl0rKVwiLFwibVwiKSk7aWYocSl7dy5jaXBoZXI9cVsxXTt3Lml2c2FsdD1xWzJdfXZhciBwPXYubWF0Y2gobmV3IFJlZ0V4cChcIi0tLS0tQkVHSU4gKFtBLVpdKykgUFJJVkFURSBLRVktLS0tLVwiKSk7aWYocCl7dy50eXBlPXBbMV19dmFyIHU9LTE7dmFyIHg9MDtpZih2LmluZGV4T2YoXCJcXHJcXG5cXHJcXG5cIikhPS0xKXt1PXYuaW5kZXhPZihcIlxcclxcblxcclxcblwiKTt4PTJ9aWYodi5pbmRleE9mKFwiXFxuXFxuXCIpIT0tMSl7dT12LmluZGV4T2YoXCJcXG5cXG5cIik7eD0xfXZhciB0PXYuaW5kZXhPZihcIi0tLS0tRU5EXCIpO2lmKHUhPS0xJiZ0IT0tMSl7dmFyIHI9di5zdWJzdHJpbmcodSt4KjIsdC14KTtyPXIucmVwbGFjZSgvXFxzKy9nLFwiXCIpO3cuZGF0YT1yfXJldHVybiB3fTt2YXIgaj1mdW5jdGlvbihxLHkscCl7dmFyIHY9cC5zdWJzdHJpbmcoMCwxNik7dmFyIHQ9Q3J5cHRvSlMuZW5jLkhleC5wYXJzZSh2KTt2YXIgcj1DcnlwdG9KUy5lbmMuVXRmOC5wYXJzZSh5KTt2YXIgdT1pW3FdW1wia2V5bGVuXCJdK2lbcV1bXCJpdmxlblwiXTt2YXIgeD1cIlwiO3ZhciB3PW51bGw7Zm9yKDs7KXt2YXIgcz1DcnlwdG9KUy5hbGdvLk1ENS5jcmVhdGUoKTtpZih3IT1udWxsKXtzLnVwZGF0ZSh3KX1zLnVwZGF0ZShyKTtzLnVwZGF0ZSh0KTt3PXMuZmluYWxpemUoKTt4PXgrQ3J5cHRvSlMuZW5jLkhleC5zdHJpbmdpZnkodyk7aWYoeC5sZW5ndGg+PXUqMil7YnJlYWt9fXZhciB6PXt9O3oua2V5aGV4PXguc3Vic3RyKDAsaVtxXVtcImtleWxlblwiXSoyKTt6Lml2aGV4PXguc3Vic3RyKGlbcV1bXCJrZXlsZW5cIl0qMixpW3FdW1wiaXZsZW5cIl0qMik7cmV0dXJuIHp9O3ZhciBiPWZ1bmN0aW9uKHAsdixyLHcpe3ZhciBzPUNyeXB0b0pTLmVuYy5CYXNlNjQucGFyc2UocCk7dmFyIHE9Q3J5cHRvSlMuZW5jLkhleC5zdHJpbmdpZnkocyk7dmFyIHU9aVt2XVtcInByb2NcIl07dmFyIHQ9dShxLHIsdyk7cmV0dXJuIHR9O3ZhciBoPWZ1bmN0aW9uKHAscyxxLHUpe3ZhciByPWlbc11bXCJlcHJvY1wiXTt2YXIgdD1yKHAscSx1KTtyZXR1cm4gdH07cmV0dXJue3ZlcnNpb246XCIxLjAuMFwiLHBhcnNlUEtDUzVQRU06ZnVuY3Rpb24ocCl7cmV0dXJuIG4ocCl9LGdldEtleUFuZFVudXNlZEl2QnlQYXNzY29kZUFuZEl2c2FsdDpmdW5jdGlvbihxLHAscil7cmV0dXJuIGoocSxwLHIpfSxkZWNyeXB0S2V5QjY0OmZ1bmN0aW9uKHAscixxLHMpe3JldHVybiBiKHAscixxLHMpfSxnZXREZWNyeXB0ZWRLZXlIZXg6ZnVuY3Rpb24oeSx4KXt2YXIgcT1uKHkpO3ZhciB0PXEudHlwZTt2YXIgcj1xLmNpcGhlcjt2YXIgcD1xLml2c2FsdDt2YXIgcz1xLmRhdGE7dmFyIHc9aihyLHgscCk7dmFyIHY9dy5rZXloZXg7dmFyIHU9YihzLHIsdixwKTtyZXR1cm4gdX0sZ2V0RW5jcnlwdGVkUEtDUzVQRU1Gcm9tUHJ2S2V5SGV4OmZ1bmN0aW9uKHgscyxBLHQscil7dmFyIHA9XCJcIjtpZih0eXBlb2YgdD09XCJ1bmRlZmluZWRcInx8dD09bnVsbCl7dD1cIkFFUy0yNTYtQ0JDXCJ9aWYodHlwZW9mIGlbdF09PVwidW5kZWZpbmVkXCIpe3Rocm93XCJLRVlVVElMIHVuc3VwcG9ydGVkIGFsZ29yaXRobTogXCIrdH1pZih0eXBlb2Ygcj09XCJ1bmRlZmluZWRcInx8cj09bnVsbCl7dmFyIHY9aVt0XVtcIml2bGVuXCJdO3ZhciB1PW0odik7cj11LnRvVXBwZXJDYXNlKCl9dmFyIHo9aih0LEEscik7dmFyIHk9ei5rZXloZXg7dmFyIHc9aChzLHQseSxyKTt2YXIgcT13LnJlcGxhY2UoLyguezY0fSkvZyxcIiQxXFxyXFxuXCIpO3ZhciBwPVwiLS0tLS1CRUdJTiBcIit4K1wiIFBSSVZBVEUgS0VZLS0tLS1cXHJcXG5cIjtwKz1cIlByb2MtVHlwZTogNCxFTkNSWVBURURcXHJcXG5cIjtwKz1cIkRFSy1JbmZvOiBcIit0K1wiLFwiK3IrXCJcXHJcXG5cIjtwKz1cIlxcclxcblwiO3ArPXE7cCs9XCJcXHJcXG4tLS0tLUVORCBcIit4K1wiIFBSSVZBVEUgS0VZLS0tLS1cXHJcXG5cIjtyZXR1cm4gcH0scGFyc2VIZXhPZkVuY3J5cHRlZFBLQ1M4OmZ1bmN0aW9uKHkpe3ZhciBCPUFTTjFIRVg7dmFyIHo9Qi5nZXRDaGlsZElkeDt2YXIgdz1CLmdldFY7dmFyIHQ9e307dmFyIHI9eih5LDApO2lmKHIubGVuZ3RoIT0yKXt0aHJvd1wibWFsZm9ybWVkIGZvcm1hdDogU0VRVUVOQ0UoMCkuaXRlbXMgIT0gMjogXCIrci5sZW5ndGh9dC5jaXBoZXJ0ZXh0PXcoeSxyWzFdKTt2YXIgQT16KHksclswXSk7aWYoQS5sZW5ndGghPTIpe3Rocm93XCJtYWxmb3JtZWQgZm9ybWF0OiBTRVFVRU5DRSgwLjApLml0ZW1zICE9IDI6IFwiK0EubGVuZ3RofWlmKHcoeSxBWzBdKSE9XCIyYTg2NDg4NmY3MGQwMTA1MGRcIil7dGhyb3dcInRoaXMgb25seSBzdXBwb3J0cyBwa2NzNVBCRVMyXCJ9dmFyIHA9eih5LEFbMV0pO2lmKEEubGVuZ3RoIT0yKXt0aHJvd1wibWFsZm9ybWVkIGZvcm1hdDogU0VRVUVOQ0UoMC4wLjEpLml0ZW1zICE9IDI6IFwiK3AubGVuZ3RofXZhciBxPXooeSxwWzFdKTtpZihxLmxlbmd0aCE9Mil7dGhyb3dcIm1hbGZvcm1lZCBmb3JtYXQ6IFNFUVVFTkNFKDAuMC4xLjEpLml0ZW1zICE9IDI6IFwiK3EubGVuZ3RofWlmKHcoeSxxWzBdKSE9XCIyYTg2NDg4NmY3MGQwMzA3XCIpe3Rocm93XCJ0aGlzIG9ubHkgc3VwcG9ydHMgVHJpcGxlREVTXCJ9dC5lbmNyeXB0aW9uU2NoZW1lQWxnPVwiVHJpcGxlREVTXCI7dC5lbmNyeXB0aW9uU2NoZW1lSVY9dyh5LHFbMV0pO3ZhciBzPXooeSxwWzBdKTtpZihzLmxlbmd0aCE9Mil7dGhyb3dcIm1hbGZvcm1lZCBmb3JtYXQ6IFNFUVVFTkNFKDAuMC4xLjApLml0ZW1zICE9IDI6IFwiK3MubGVuZ3RofWlmKHcoeSxzWzBdKSE9XCIyYTg2NDg4NmY3MGQwMTA1MGNcIil7dGhyb3dcInRoaXMgb25seSBzdXBwb3J0cyBwa2NzNVBCS0RGMlwifXZhciB4PXooeSxzWzFdKTtpZih4Lmxlbmd0aDwyKXt0aHJvd1wibWFsZm9ybWVkIGZvcm1hdDogU0VRVUVOQ0UoMC4wLjEuMC4xKS5pdGVtcyA8IDI6IFwiK3gubGVuZ3RofXQucGJrZGYyU2FsdD13KHkseFswXSk7dmFyIHU9dyh5LHhbMV0pO3RyeXt0LnBia2RmMkl0ZXI9cGFyc2VJbnQodSwxNil9Y2F0Y2godil7dGhyb3dcIm1hbGZvcm1lZCBmb3JtYXQgcGJrZGYySXRlcjogXCIrdX1yZXR1cm4gdH0sZ2V0UEJLREYyS2V5SGV4RnJvbVBhcmFtOmZ1bmN0aW9uKHUscCl7dmFyIHQ9Q3J5cHRvSlMuZW5jLkhleC5wYXJzZSh1LnBia2RmMlNhbHQpO3ZhciBxPXUucGJrZGYySXRlcjt2YXIgcz1DcnlwdG9KUy5QQktERjIocCx0LHtrZXlTaXplOjE5Mi8zMixpdGVyYXRpb25zOnF9KTt2YXIgcj1DcnlwdG9KUy5lbmMuSGV4LnN0cmluZ2lmeShzKTtyZXR1cm4gcn0sX2dldFBsYWluUEtDUzhIZXhGcm9tRW5jcnlwdGVkUEtDUzhQRU06ZnVuY3Rpb24oeCx5KXt2YXIgcj1wZW10b2hleCh4LFwiRU5DUllQVEVEIFBSSVZBVEUgS0VZXCIpO3ZhciBwPXRoaXMucGFyc2VIZXhPZkVuY3J5cHRlZFBLQ1M4KHIpO3ZhciB1PUtFWVVUSUwuZ2V0UEJLREYyS2V5SGV4RnJvbVBhcmFtKHAseSk7dmFyIHY9e307di5jaXBoZXJ0ZXh0PUNyeXB0b0pTLmVuYy5IZXgucGFyc2UocC5jaXBoZXJ0ZXh0KTt2YXIgdD1DcnlwdG9KUy5lbmMuSGV4LnBhcnNlKHUpO3ZhciBzPUNyeXB0b0pTLmVuYy5IZXgucGFyc2UocC5lbmNyeXB0aW9uU2NoZW1lSVYpO3ZhciB3PUNyeXB0b0pTLlRyaXBsZURFUy5kZWNyeXB0KHYsdCx7aXY6c30pO3ZhciBxPUNyeXB0b0pTLmVuYy5IZXguc3RyaW5naWZ5KHcpO3JldHVybiBxfSxnZXRLZXlGcm9tRW5jcnlwdGVkUEtDUzhQRU06ZnVuY3Rpb24ocyxxKXt2YXIgcD10aGlzLl9nZXRQbGFpblBLQ1M4SGV4RnJvbUVuY3J5cHRlZFBLQ1M4UEVNKHMscSk7dmFyIHI9dGhpcy5nZXRLZXlGcm9tUGxhaW5Qcml2YXRlUEtDUzhIZXgocCk7cmV0dXJuIHJ9LHBhcnNlUGxhaW5Qcml2YXRlUEtDUzhIZXg6ZnVuY3Rpb24ocyl7dmFyIHY9QVNOMUhFWDt2YXIgdT12LmdldENoaWxkSWR4O3ZhciB0PXYuZ2V0Vjt2YXIgcT17fTtxLmFsZ3BhcmFtPW51bGw7aWYocy5zdWJzdHIoMCwyKSE9XCIzMFwiKXt0aHJvd1wibWFsZm9ybWVkIHBsYWluIFBLQ1M4IHByaXZhdGUga2V5KGNvZGU6MDAxKVwifXZhciByPXUocywwKTtpZihyLmxlbmd0aCE9Myl7dGhyb3dcIm1hbGZvcm1lZCBwbGFpbiBQS0NTOCBwcml2YXRlIGtleShjb2RlOjAwMilcIn1pZihzLnN1YnN0cihyWzFdLDIpIT1cIjMwXCIpe3Rocm93XCJtYWxmb3JtZWQgUEtDUzggcHJpdmF0ZSBrZXkoY29kZTowMDMpXCJ9dmFyIHA9dShzLHJbMV0pO2lmKHAubGVuZ3RoIT0yKXt0aHJvd1wibWFsZm9ybWVkIFBLQ1M4IHByaXZhdGUga2V5KGNvZGU6MDA0KVwifWlmKHMuc3Vic3RyKHBbMF0sMikhPVwiMDZcIil7dGhyb3dcIm1hbGZvcm1lZCBQS0NTOCBwcml2YXRlIGtleShjb2RlOjAwNSlcIn1xLmFsZ29pZD10KHMscFswXSk7aWYocy5zdWJzdHIocFsxXSwyKT09XCIwNlwiKXtxLmFsZ3BhcmFtPXQocyxwWzFdKX1pZihzLnN1YnN0cihyWzJdLDIpIT1cIjA0XCIpe3Rocm93XCJtYWxmb3JtZWQgUEtDUzggcHJpdmF0ZSBrZXkoY29kZTowMDYpXCJ9cS5rZXlpZHg9di5nZXRWaWR4KHMsclsyXSk7cmV0dXJuIHF9LGdldEtleUZyb21QbGFpblByaXZhdGVQS0NTOFBFTTpmdW5jdGlvbihxKXt2YXIgcD1wZW10b2hleChxLFwiUFJJVkFURSBLRVlcIik7dmFyIHI9dGhpcy5nZXRLZXlGcm9tUGxhaW5Qcml2YXRlUEtDUzhIZXgocCk7cmV0dXJuIHJ9LGdldEtleUZyb21QbGFpblByaXZhdGVQS0NTOEhleDpmdW5jdGlvbihwKXt2YXIgcT10aGlzLnBhcnNlUGxhaW5Qcml2YXRlUEtDUzhIZXgocCk7dmFyIHI7aWYocS5hbGdvaWQ9PVwiMmE4NjQ4ODZmNzBkMDEwMTAxXCIpe3I9bmV3IFJTQUtleSgpfWVsc2V7aWYocS5hbGdvaWQ9PVwiMmE4NjQ4Y2UzODA0MDFcIil7cj1uZXcgS0pVUi5jcnlwdG8uRFNBKCl9ZWxzZXtpZihxLmFsZ29pZD09XCIyYTg2NDhjZTNkMDIwMVwiKXtyPW5ldyBLSlVSLmNyeXB0by5FQ0RTQSgpfWVsc2V7dGhyb3dcInVuc3VwcG9ydGVkIHByaXZhdGUga2V5IGFsZ29yaXRobVwifX19ci5yZWFkUEtDUzhQcnZLZXlIZXgocCk7cmV0dXJuIHJ9LF9nZXRLZXlGcm9tUHVibGljUEtDUzhIZXg6ZnVuY3Rpb24ocSl7dmFyIHA7dmFyIHI9QVNOMUhFWC5nZXRWYnlMaXN0KHEsMCxbMCwwXSxcIjA2XCIpO2lmKHI9PT1cIjJhODY0ODg2ZjcwZDAxMDEwMVwiKXtwPW5ldyBSU0FLZXkoKX1lbHNle2lmKHI9PT1cIjJhODY0OGNlMzgwNDAxXCIpe3A9bmV3IEtKVVIuY3J5cHRvLkRTQSgpfWVsc2V7aWYocj09PVwiMmE4NjQ4Y2UzZDAyMDFcIil7cD1uZXcgS0pVUi5jcnlwdG8uRUNEU0EoKX1lbHNle3Rocm93XCJ1bnN1cHBvcnRlZCBQS0NTIzggcHVibGljIGtleSBoZXhcIn19fXAucmVhZFBLQ1M4UHViS2V5SGV4KHEpO3JldHVybiBwfSxwYXJzZVB1YmxpY1Jhd1JTQUtleUhleDpmdW5jdGlvbihyKXt2YXIgdT1BU04xSEVYO3ZhciB0PXUuZ2V0Q2hpbGRJZHg7dmFyIHM9dS5nZXRWO3ZhciBwPXt9O2lmKHIuc3Vic3RyKDAsMikhPVwiMzBcIil7dGhyb3dcIm1hbGZvcm1lZCBSU0Ega2V5KGNvZGU6MDAxKVwifXZhciBxPXQociwwKTtpZihxLmxlbmd0aCE9Mil7dGhyb3dcIm1hbGZvcm1lZCBSU0Ega2V5KGNvZGU6MDAyKVwifWlmKHIuc3Vic3RyKHFbMF0sMikhPVwiMDJcIil7dGhyb3dcIm1hbGZvcm1lZCBSU0Ega2V5KGNvZGU6MDAzKVwifXAubj1zKHIscVswXSk7aWYoci5zdWJzdHIocVsxXSwyKSE9XCIwMlwiKXt0aHJvd1wibWFsZm9ybWVkIFJTQSBrZXkoY29kZTowMDQpXCJ9cC5lPXMocixxWzFdKTtyZXR1cm4gcH0scGFyc2VQdWJsaWNQS0NTOEhleDpmdW5jdGlvbih0KXt2YXIgdj1BU04xSEVYO3ZhciB1PXYuZ2V0Q2hpbGRJZHg7dmFyIHM9di5nZXRWO3ZhciBxPXt9O3EuYWxncGFyYW09bnVsbDt2YXIgcj11KHQsMCk7aWYoci5sZW5ndGghPTIpe3Rocm93XCJvdXRlciBERVJTZXF1ZW5jZSBzaGFsbCBoYXZlIDIgZWxlbWVudHM6IFwiK3IubGVuZ3RofXZhciB3PXJbMF07aWYodC5zdWJzdHIodywyKSE9XCIzMFwiKXt0aHJvd1wibWFsZm9ybWVkIFBLQ1M4IHB1YmxpYyBrZXkoY29kZTowMDEpXCJ9dmFyIHA9dSh0LHcpO2lmKHAubGVuZ3RoIT0yKXt0aHJvd1wibWFsZm9ybWVkIFBLQ1M4IHB1YmxpYyBrZXkoY29kZTowMDIpXCJ9aWYodC5zdWJzdHIocFswXSwyKSE9XCIwNlwiKXt0aHJvd1wibWFsZm9ybWVkIFBLQ1M4IHB1YmxpYyBrZXkoY29kZTowMDMpXCJ9cS5hbGdvaWQ9cyh0LHBbMF0pO2lmKHQuc3Vic3RyKHBbMV0sMik9PVwiMDZcIil7cS5hbGdwYXJhbT1zKHQscFsxXSl9ZWxzZXtpZih0LnN1YnN0cihwWzFdLDIpPT1cIjMwXCIpe3EuYWxncGFyYW09e307cS5hbGdwYXJhbS5wPXYuZ2V0VmJ5TGlzdCh0LHBbMV0sWzBdLFwiMDJcIik7cS5hbGdwYXJhbS5xPXYuZ2V0VmJ5TGlzdCh0LHBbMV0sWzFdLFwiMDJcIik7cS5hbGdwYXJhbS5nPXYuZ2V0VmJ5TGlzdCh0LHBbMV0sWzJdLFwiMDJcIil9fWlmKHQuc3Vic3RyKHJbMV0sMikhPVwiMDNcIil7dGhyb3dcIm1hbGZvcm1lZCBQS0NTOCBwdWJsaWMga2V5KGNvZGU6MDA0KVwifXEua2V5PXModCxyWzFdKS5zdWJzdHIoMik7cmV0dXJuIHF9LH19KCk7S0VZVVRJTC5nZXRLZXk9ZnVuY3Rpb24obCxrLG4pe3ZhciBHPUFTTjFIRVgsTD1HLmdldENoaWxkSWR4LHY9Ry5nZXRWLGQ9Ry5nZXRWYnlMaXN0LGM9S0pVUi5jcnlwdG8saT1jLkVDRFNBLEM9Yy5EU0Esdz1SU0FLZXksTT1wZW10b2hleCxGPUtFWVVUSUw7aWYodHlwZW9mIHchPVwidW5kZWZpbmVkXCImJmwgaW5zdGFuY2VvZiB3KXtyZXR1cm4gbH1pZih0eXBlb2YgaSE9XCJ1bmRlZmluZWRcIiYmbCBpbnN0YW5jZW9mIGkpe3JldHVybiBsfWlmKHR5cGVvZiBDIT1cInVuZGVmaW5lZFwiJiZsIGluc3RhbmNlb2YgQyl7cmV0dXJuIGx9aWYobC5jdXJ2ZSE9PXVuZGVmaW5lZCYmbC54eSE9PXVuZGVmaW5lZCYmbC5kPT09dW5kZWZpbmVkKXtyZXR1cm4gbmV3IGkoe3B1YjpsLnh5LGN1cnZlOmwuY3VydmV9KX1pZihsLmN1cnZlIT09dW5kZWZpbmVkJiZsLmQhPT11bmRlZmluZWQpe3JldHVybiBuZXcgaSh7cHJ2OmwuZCxjdXJ2ZTpsLmN1cnZlfSl9aWYobC5rdHk9PT11bmRlZmluZWQmJmwubiE9PXVuZGVmaW5lZCYmbC5lIT09dW5kZWZpbmVkJiZsLmQ9PT11bmRlZmluZWQpe3ZhciBQPW5ldyB3KCk7UC5zZXRQdWJsaWMobC5uLGwuZSk7cmV0dXJuIFB9aWYobC5rdHk9PT11bmRlZmluZWQmJmwubiE9PXVuZGVmaW5lZCYmbC5lIT09dW5kZWZpbmVkJiZsLmQhPT11bmRlZmluZWQmJmwucCE9PXVuZGVmaW5lZCYmbC5xIT09dW5kZWZpbmVkJiZsLmRwIT09dW5kZWZpbmVkJiZsLmRxIT09dW5kZWZpbmVkJiZsLmNvIT09dW5kZWZpbmVkJiZsLnFpPT09dW5kZWZpbmVkKXt2YXIgUD1uZXcgdygpO1Auc2V0UHJpdmF0ZUV4KGwubixsLmUsbC5kLGwucCxsLnEsbC5kcCxsLmRxLGwuY28pO3JldHVybiBQfWlmKGwua3R5PT09dW5kZWZpbmVkJiZsLm4hPT11bmRlZmluZWQmJmwuZSE9PXVuZGVmaW5lZCYmbC5kIT09dW5kZWZpbmVkJiZsLnA9PT11bmRlZmluZWQpe3ZhciBQPW5ldyB3KCk7UC5zZXRQcml2YXRlKGwubixsLmUsbC5kKTtyZXR1cm4gUH1pZihsLnAhPT11bmRlZmluZWQmJmwucSE9PXVuZGVmaW5lZCYmbC5nIT09dW5kZWZpbmVkJiZsLnkhPT11bmRlZmluZWQmJmwueD09PXVuZGVmaW5lZCl7dmFyIFA9bmV3IEMoKTtQLnNldFB1YmxpYyhsLnAsbC5xLGwuZyxsLnkpO3JldHVybiBQfWlmKGwucCE9PXVuZGVmaW5lZCYmbC5xIT09dW5kZWZpbmVkJiZsLmchPT11bmRlZmluZWQmJmwueSE9PXVuZGVmaW5lZCYmbC54IT09dW5kZWZpbmVkKXt2YXIgUD1uZXcgQygpO1Auc2V0UHJpdmF0ZShsLnAsbC5xLGwuZyxsLnksbC54KTtyZXR1cm4gUH1pZihsLmt0eT09PVwiUlNBXCImJmwubiE9PXVuZGVmaW5lZCYmbC5lIT09dW5kZWZpbmVkJiZsLmQ9PT11bmRlZmluZWQpe3ZhciBQPW5ldyB3KCk7UC5zZXRQdWJsaWMoYjY0dXRvaGV4KGwubiksYjY0dXRvaGV4KGwuZSkpO3JldHVybiBQfWlmKGwua3R5PT09XCJSU0FcIiYmbC5uIT09dW5kZWZpbmVkJiZsLmUhPT11bmRlZmluZWQmJmwuZCE9PXVuZGVmaW5lZCYmbC5wIT09dW5kZWZpbmVkJiZsLnEhPT11bmRlZmluZWQmJmwuZHAhPT11bmRlZmluZWQmJmwuZHEhPT11bmRlZmluZWQmJmwucWkhPT11bmRlZmluZWQpe3ZhciBQPW5ldyB3KCk7UC5zZXRQcml2YXRlRXgoYjY0dXRvaGV4KGwubiksYjY0dXRvaGV4KGwuZSksYjY0dXRvaGV4KGwuZCksYjY0dXRvaGV4KGwucCksYjY0dXRvaGV4KGwucSksYjY0dXRvaGV4KGwuZHApLGI2NHV0b2hleChsLmRxKSxiNjR1dG9oZXgobC5xaSkpO3JldHVybiBQfWlmKGwua3R5PT09XCJSU0FcIiYmbC5uIT09dW5kZWZpbmVkJiZsLmUhPT11bmRlZmluZWQmJmwuZCE9PXVuZGVmaW5lZCl7dmFyIFA9bmV3IHcoKTtQLnNldFByaXZhdGUoYjY0dXRvaGV4KGwubiksYjY0dXRvaGV4KGwuZSksYjY0dXRvaGV4KGwuZCkpO3JldHVybiBQfWlmKGwua3R5PT09XCJFQ1wiJiZsLmNydiE9PXVuZGVmaW5lZCYmbC54IT09dW5kZWZpbmVkJiZsLnkhPT11bmRlZmluZWQmJmwuZD09PXVuZGVmaW5lZCl7dmFyIGo9bmV3IGkoe2N1cnZlOmwuY3J2fSk7dmFyIHQ9ai5lY3BhcmFtcy5rZXlsZW4vNDt2YXIgQj0oXCIwMDAwMDAwMDAwXCIrYjY0dXRvaGV4KGwueCkpLnNsaWNlKC10KTt2YXIgej0oXCIwMDAwMDAwMDAwXCIrYjY0dXRvaGV4KGwueSkpLnNsaWNlKC10KTt2YXIgdT1cIjA0XCIrQit6O2ouc2V0UHVibGljS2V5SGV4KHUpO3JldHVybiBqfWlmKGwua3R5PT09XCJFQ1wiJiZsLmNydiE9PXVuZGVmaW5lZCYmbC54IT09dW5kZWZpbmVkJiZsLnkhPT11bmRlZmluZWQmJmwuZCE9PXVuZGVmaW5lZCl7dmFyIGo9bmV3IGkoe2N1cnZlOmwuY3J2fSk7dmFyIHQ9ai5lY3BhcmFtcy5rZXlsZW4vNDt2YXIgQj0oXCIwMDAwMDAwMDAwXCIrYjY0dXRvaGV4KGwueCkpLnNsaWNlKC10KTt2YXIgej0oXCIwMDAwMDAwMDAwXCIrYjY0dXRvaGV4KGwueSkpLnNsaWNlKC10KTt2YXIgdT1cIjA0XCIrQit6O3ZhciBiPShcIjAwMDAwMDAwMDBcIitiNjR1dG9oZXgobC5kKSkuc2xpY2UoLXQpO2ouc2V0UHVibGljS2V5SGV4KHUpO2ouc2V0UHJpdmF0ZUtleUhleChiKTtyZXR1cm4gan1pZihuPT09XCJwa2NzNXBydlwiKXt2YXIgSj1sLEc9QVNOMUhFWCxOLFA7Tj1MKEosMCk7aWYoTi5sZW5ndGg9PT05KXtQPW5ldyB3KCk7UC5yZWFkUEtDUzVQcnZLZXlIZXgoSil9ZWxzZXtpZihOLmxlbmd0aD09PTYpe1A9bmV3IEMoKTtQLnJlYWRQS0NTNVBydktleUhleChKKX1lbHNle2lmKE4ubGVuZ3RoPjImJkouc3Vic3RyKE5bMV0sMik9PT1cIjA0XCIpe1A9bmV3IGkoKTtQLnJlYWRQS0NTNVBydktleUhleChKKX1lbHNle3Rocm93XCJ1bnN1cHBvcnRlZCBQS0NTIzEvNSBoZXhhZGVjaW1hbCBrZXlcIn19fXJldHVybiBQfWlmKG49PT1cInBrY3M4cHJ2XCIpe3ZhciBQPUYuZ2V0S2V5RnJvbVBsYWluUHJpdmF0ZVBLQ1M4SGV4KGwpO3JldHVybiBQfWlmKG49PT1cInBrY3M4cHViXCIpe3JldHVybiBGLl9nZXRLZXlGcm9tUHVibGljUEtDUzhIZXgobCl9aWYobj09PVwieDUwOXB1YlwiKXtyZXR1cm4gWDUwOS5nZXRQdWJsaWNLZXlGcm9tQ2VydEhleChsKX1pZihsLmluZGV4T2YoXCItRU5EIENFUlRJRklDQVRFLVwiLDApIT0tMXx8bC5pbmRleE9mKFwiLUVORCBYNTA5IENFUlRJRklDQVRFLVwiLDApIT0tMXx8bC5pbmRleE9mKFwiLUVORCBUUlVTVEVEIENFUlRJRklDQVRFLVwiLDApIT0tMSl7cmV0dXJuIFg1MDkuZ2V0UHVibGljS2V5RnJvbUNlcnRQRU0obCl9aWYobC5pbmRleE9mKFwiLUVORCBQVUJMSUMgS0VZLVwiKSE9LTEpe3ZhciBPPXBlbXRvaGV4KGwsXCJQVUJMSUMgS0VZXCIpO3JldHVybiBGLl9nZXRLZXlGcm9tUHVibGljUEtDUzhIZXgoTyl9aWYobC5pbmRleE9mKFwiLUVORCBSU0EgUFJJVkFURSBLRVktXCIpIT0tMSYmbC5pbmRleE9mKFwiNCxFTkNSWVBURURcIik9PS0xKXt2YXIgbT1NKGwsXCJSU0EgUFJJVkFURSBLRVlcIik7cmV0dXJuIEYuZ2V0S2V5KG0sbnVsbCxcInBrY3M1cHJ2XCIpfWlmKGwuaW5kZXhPZihcIi1FTkQgRFNBIFBSSVZBVEUgS0VZLVwiKSE9LTEmJmwuaW5kZXhPZihcIjQsRU5DUllQVEVEXCIpPT0tMSl7dmFyIEk9TShsLFwiRFNBIFBSSVZBVEUgS0VZXCIpO3ZhciBFPWQoSSwwLFsxXSxcIjAyXCIpO3ZhciBEPWQoSSwwLFsyXSxcIjAyXCIpO3ZhciBLPWQoSSwwLFszXSxcIjAyXCIpO3ZhciByPWQoSSwwLFs0XSxcIjAyXCIpO3ZhciBzPWQoSSwwLFs1XSxcIjAyXCIpO3ZhciBQPW5ldyBDKCk7UC5zZXRQcml2YXRlKG5ldyBCaWdJbnRlZ2VyKEUsMTYpLG5ldyBCaWdJbnRlZ2VyKEQsMTYpLG5ldyBCaWdJbnRlZ2VyKEssMTYpLG5ldyBCaWdJbnRlZ2VyKHIsMTYpLG5ldyBCaWdJbnRlZ2VyKHMsMTYpKTtyZXR1cm4gUH1pZihsLmluZGV4T2YoXCItRU5EIFBSSVZBVEUgS0VZLVwiKSE9LTEpe3JldHVybiBGLmdldEtleUZyb21QbGFpblByaXZhdGVQS0NTOFBFTShsKX1pZihsLmluZGV4T2YoXCItRU5EIFJTQSBQUklWQVRFIEtFWS1cIikhPS0xJiZsLmluZGV4T2YoXCI0LEVOQ1JZUFRFRFwiKSE9LTEpe3ZhciBvPUYuZ2V0RGVjcnlwdGVkS2V5SGV4KGwsayk7dmFyIEg9bmV3IFJTQUtleSgpO0gucmVhZFBLQ1M1UHJ2S2V5SGV4KG8pO3JldHVybiBIfWlmKGwuaW5kZXhPZihcIi1FTkQgRUMgUFJJVkFURSBLRVktXCIpIT0tMSYmbC5pbmRleE9mKFwiNCxFTkNSWVBURURcIikhPS0xKXt2YXIgST1GLmdldERlY3J5cHRlZEtleUhleChsLGspO3ZhciBQPWQoSSwwLFsxXSxcIjA0XCIpO3ZhciBmPWQoSSwwLFsyLDBdLFwiMDZcIik7dmFyIEE9ZChJLDAsWzMsMF0sXCIwM1wiKS5zdWJzdHIoMik7dmFyIGU9XCJcIjtpZihLSlVSLmNyeXB0by5PSUQub2lkaGV4Mm5hbWVbZl0hPT11bmRlZmluZWQpe2U9S0pVUi5jcnlwdG8uT0lELm9pZGhleDJuYW1lW2ZdfWVsc2V7dGhyb3dcInVuZGVmaW5lZCBPSUQoaGV4KSBpbiBLSlVSLmNyeXB0by5PSUQ6IFwiK2Z9dmFyIGo9bmV3IGkoe2N1cnZlOmV9KTtqLnNldFB1YmxpY0tleUhleChBKTtqLnNldFByaXZhdGVLZXlIZXgoUCk7ai5pc1B1YmxpYz1mYWxzZTtyZXR1cm4gan1pZihsLmluZGV4T2YoXCItRU5EIERTQSBQUklWQVRFIEtFWS1cIikhPS0xJiZsLmluZGV4T2YoXCI0LEVOQ1JZUFRFRFwiKSE9LTEpe3ZhciBJPUYuZ2V0RGVjcnlwdGVkS2V5SGV4KGwsayk7dmFyIEU9ZChJLDAsWzFdLFwiMDJcIik7dmFyIEQ9ZChJLDAsWzJdLFwiMDJcIik7dmFyIEs9ZChJLDAsWzNdLFwiMDJcIik7dmFyIHI9ZChJLDAsWzRdLFwiMDJcIik7dmFyIHM9ZChJLDAsWzVdLFwiMDJcIik7dmFyIFA9bmV3IEMoKTtQLnNldFByaXZhdGUobmV3IEJpZ0ludGVnZXIoRSwxNiksbmV3IEJpZ0ludGVnZXIoRCwxNiksbmV3IEJpZ0ludGVnZXIoSywxNiksbmV3IEJpZ0ludGVnZXIociwxNiksbmV3IEJpZ0ludGVnZXIocywxNikpO3JldHVybiBQfWlmKGwuaW5kZXhPZihcIi1FTkQgRU5DUllQVEVEIFBSSVZBVEUgS0VZLVwiKSE9LTEpe3JldHVybiBGLmdldEtleUZyb21FbmNyeXB0ZWRQS0NTOFBFTShsLGspfXRocm93XCJub3Qgc3VwcG9ydGVkIGFyZ3VtZW50XCJ9O0tFWVVUSUwuZ2VuZXJhdGVLZXlwYWlyPWZ1bmN0aW9uKGEsYyl7aWYoYT09XCJSU0FcIil7dmFyIGI9Yzt2YXIgaD1uZXcgUlNBS2V5KCk7aC5nZW5lcmF0ZShiLFwiMTAwMDFcIik7aC5pc1ByaXZhdGU9dHJ1ZTtoLmlzUHVibGljPXRydWU7dmFyIGY9bmV3IFJTQUtleSgpO3ZhciBlPWgubi50b1N0cmluZygxNik7dmFyIGk9aC5lLnRvU3RyaW5nKDE2KTtmLnNldFB1YmxpYyhlLGkpO2YuaXNQcml2YXRlPWZhbHNlO2YuaXNQdWJsaWM9dHJ1ZTt2YXIgaz17fTtrLnBydktleU9iaj1oO2sucHViS2V5T2JqPWY7cmV0dXJuIGt9ZWxzZXtpZihhPT1cIkVDXCIpe3ZhciBkPWM7dmFyIGc9bmV3IEtKVVIuY3J5cHRvLkVDRFNBKHtjdXJ2ZTpkfSk7dmFyIGo9Zy5nZW5lcmF0ZUtleVBhaXJIZXgoKTt2YXIgaD1uZXcgS0pVUi5jcnlwdG8uRUNEU0Eoe2N1cnZlOmR9KTtoLnNldFB1YmxpY0tleUhleChqLmVjcHViaGV4KTtoLnNldFByaXZhdGVLZXlIZXgoai5lY3BydmhleCk7aC5pc1ByaXZhdGU9dHJ1ZTtoLmlzUHVibGljPWZhbHNlO3ZhciBmPW5ldyBLSlVSLmNyeXB0by5FQ0RTQSh7Y3VydmU6ZH0pO2Yuc2V0UHVibGljS2V5SGV4KGouZWNwdWJoZXgpO2YuaXNQcml2YXRlPWZhbHNlO2YuaXNQdWJsaWM9dHJ1ZTt2YXIgaz17fTtrLnBydktleU9iaj1oO2sucHViS2V5T2JqPWY7cmV0dXJuIGt9ZWxzZXt0aHJvd1widW5rbm93biBhbGdvcml0aG06IFwiK2F9fX07S0VZVVRJTC5nZXRQRU09ZnVuY3Rpb24oYixELHksbSxxLGope3ZhciBGPUtKVVIsaz1GLmFzbjEsej1rLkRFUk9iamVjdElkZW50aWZpZXIsZj1rLkRFUkludGVnZXIsbD1rLkFTTjFVdGlsLm5ld09iamVjdCxhPWsueDUwOSxDPWEuU3ViamVjdFB1YmxpY0tleUluZm8sZT1GLmNyeXB0byx1PWUuRFNBLHI9ZS5FQ0RTQSxuPVJTQUtleTtmdW5jdGlvbiBBKHMpe3ZhciBHPWwoe3NlcTpbe1wiaW50XCI6MH0se1wiaW50XCI6e2JpZ2ludDpzLm59fSx7XCJpbnRcIjpzLmV9LHtcImludFwiOntiaWdpbnQ6cy5kfX0se1wiaW50XCI6e2JpZ2ludDpzLnB9fSx7XCJpbnRcIjp7YmlnaW50OnMucX19LHtcImludFwiOntiaWdpbnQ6cy5kbXAxfX0se1wiaW50XCI6e2JpZ2ludDpzLmRtcTF9fSx7XCJpbnRcIjp7YmlnaW50OnMuY29lZmZ9fV19KTtyZXR1cm4gR31mdW5jdGlvbiBCKEcpe3ZhciBzPWwoe3NlcTpbe1wiaW50XCI6MX0se29jdHN0cjp7aGV4OkcucHJ2S2V5SGV4fX0se3RhZzpbXCJhMFwiLHRydWUse29pZDp7bmFtZTpHLmN1cnZlTmFtZX19XX0se3RhZzpbXCJhMVwiLHRydWUse2JpdHN0cjp7aGV4OlwiMDBcIitHLnB1YktleUhleH19XX1dfSk7cmV0dXJuIHN9ZnVuY3Rpb24geChzKXt2YXIgRz1sKHtzZXE6W3tcImludFwiOjB9LHtcImludFwiOntiaWdpbnQ6cy5wfX0se1wiaW50XCI6e2JpZ2ludDpzLnF9fSx7XCJpbnRcIjp7YmlnaW50OnMuZ319LHtcImludFwiOntiaWdpbnQ6cy55fX0se1wiaW50XCI6e2JpZ2ludDpzLnh9fV19KTtyZXR1cm4gR31pZigoKG4hPT11bmRlZmluZWQmJmIgaW5zdGFuY2VvZiBuKXx8KHUhPT11bmRlZmluZWQmJmIgaW5zdGFuY2VvZiB1KXx8KHIhPT11bmRlZmluZWQmJmIgaW5zdGFuY2VvZiByKSkmJmIuaXNQdWJsaWM9PXRydWUmJihEPT09dW5kZWZpbmVkfHxEPT1cIlBLQ1M4UFVCXCIpKXt2YXIgRT1uZXcgQyhiKTt2YXIgdz1FLmdldEVuY29kZWRIZXgoKTtyZXR1cm4gaGV4dG9wZW0odyxcIlBVQkxJQyBLRVlcIil9aWYoRD09XCJQS0NTMVBSVlwiJiZuIT09dW5kZWZpbmVkJiZiIGluc3RhbmNlb2YgbiYmKHk9PT11bmRlZmluZWR8fHk9PW51bGwpJiZiLmlzUHJpdmF0ZT09dHJ1ZSl7dmFyIEU9QShiKTt2YXIgdz1FLmdldEVuY29kZWRIZXgoKTtyZXR1cm4gaGV4dG9wZW0odyxcIlJTQSBQUklWQVRFIEtFWVwiKX1pZihEPT1cIlBLQ1MxUFJWXCImJnIhPT11bmRlZmluZWQmJmIgaW5zdGFuY2VvZiByJiYoeT09PXVuZGVmaW5lZHx8eT09bnVsbCkmJmIuaXNQcml2YXRlPT10cnVlKXt2YXIgaT1uZXcgeih7bmFtZTpiLmN1cnZlTmFtZX0pO3ZhciB2PWkuZ2V0RW5jb2RlZEhleCgpO3ZhciBoPUIoYik7dmFyIHQ9aC5nZXRFbmNvZGVkSGV4KCk7dmFyIHA9XCJcIjtwKz1oZXh0b3BlbSh2LFwiRUMgUEFSQU1FVEVSU1wiKTtwKz1oZXh0b3BlbSh0LFwiRUMgUFJJVkFURSBLRVlcIik7cmV0dXJuIHB9aWYoRD09XCJQS0NTMVBSVlwiJiZ1IT09dW5kZWZpbmVkJiZiIGluc3RhbmNlb2YgdSYmKHk9PT11bmRlZmluZWR8fHk9PW51bGwpJiZiLmlzUHJpdmF0ZT09dHJ1ZSl7dmFyIEU9eChiKTt2YXIgdz1FLmdldEVuY29kZWRIZXgoKTtyZXR1cm4gaGV4dG9wZW0odyxcIkRTQSBQUklWQVRFIEtFWVwiKX1pZihEPT1cIlBLQ1M1UFJWXCImJm4hPT11bmRlZmluZWQmJmIgaW5zdGFuY2VvZiBuJiYoeSE9PXVuZGVmaW5lZCYmeSE9bnVsbCkmJmIuaXNQcml2YXRlPT10cnVlKXt2YXIgRT1BKGIpO3ZhciB3PUUuZ2V0RW5jb2RlZEhleCgpO2lmKG09PT11bmRlZmluZWQpe209XCJERVMtRURFMy1DQkNcIn1yZXR1cm4gdGhpcy5nZXRFbmNyeXB0ZWRQS0NTNVBFTUZyb21QcnZLZXlIZXgoXCJSU0FcIix3LHksbSxqKX1pZihEPT1cIlBLQ1M1UFJWXCImJnIhPT11bmRlZmluZWQmJmIgaW5zdGFuY2VvZiByJiYoeSE9PXVuZGVmaW5lZCYmeSE9bnVsbCkmJmIuaXNQcml2YXRlPT10cnVlKXt2YXIgRT1CKGIpO3ZhciB3PUUuZ2V0RW5jb2RlZEhleCgpO2lmKG09PT11bmRlZmluZWQpe209XCJERVMtRURFMy1DQkNcIn1yZXR1cm4gdGhpcy5nZXRFbmNyeXB0ZWRQS0NTNVBFTUZyb21QcnZLZXlIZXgoXCJFQ1wiLHcseSxtLGopfWlmKEQ9PVwiUEtDUzVQUlZcIiYmdSE9PXVuZGVmaW5lZCYmYiBpbnN0YW5jZW9mIHUmJih5IT09dW5kZWZpbmVkJiZ5IT1udWxsKSYmYi5pc1ByaXZhdGU9PXRydWUpe3ZhciBFPXgoYik7dmFyIHc9RS5nZXRFbmNvZGVkSGV4KCk7aWYobT09PXVuZGVmaW5lZCl7bT1cIkRFUy1FREUzLUNCQ1wifXJldHVybiB0aGlzLmdldEVuY3J5cHRlZFBLQ1M1UEVNRnJvbVBydktleUhleChcIkRTQVwiLHcseSxtLGopfXZhciBvPWZ1bmN0aW9uKEcscyl7dmFyIEk9YyhHLHMpO3ZhciBIPW5ldyBsKHtzZXE6W3tzZXE6W3tvaWQ6e25hbWU6XCJwa2NzNVBCRVMyXCJ9fSx7c2VxOlt7c2VxOlt7b2lkOntuYW1lOlwicGtjczVQQktERjJcIn19LHtzZXE6W3tvY3RzdHI6e2hleDpJLnBia2RmMlNhbHR9fSx7XCJpbnRcIjpJLnBia2RmMkl0ZXJ9XX1dfSx7c2VxOlt7b2lkOntuYW1lOlwiZGVzLUVERTMtQ0JDXCJ9fSx7b2N0c3RyOntoZXg6SS5lbmNyeXB0aW9uU2NoZW1lSVZ9fV19XX1dfSx7b2N0c3RyOntoZXg6SS5jaXBoZXJ0ZXh0fX1dfSk7cmV0dXJuIEguZ2V0RW5jb2RlZEhleCgpfTt2YXIgYz1mdW5jdGlvbihOLE8pe3ZhciBIPTEwMDt2YXIgTT1DcnlwdG9KUy5saWIuV29yZEFycmF5LnJhbmRvbSg4KTt2YXIgTD1cIkRFUy1FREUzLUNCQ1wiO3ZhciBzPUNyeXB0b0pTLmxpYi5Xb3JkQXJyYXkucmFuZG9tKDgpO3ZhciBJPUNyeXB0b0pTLlBCS0RGMihPLE0se2tleVNpemU6MTkyLzMyLGl0ZXJhdGlvbnM6SH0pO3ZhciBKPUNyeXB0b0pTLmVuYy5IZXgucGFyc2UoTik7dmFyIEs9Q3J5cHRvSlMuVHJpcGxlREVTLmVuY3J5cHQoSixJLHtpdjpzfSkrXCJcIjt2YXIgRz17fTtHLmNpcGhlcnRleHQ9SztHLnBia2RmMlNhbHQ9Q3J5cHRvSlMuZW5jLkhleC5zdHJpbmdpZnkoTSk7Ry5wYmtkZjJJdGVyPUg7Ry5lbmNyeXB0aW9uU2NoZW1lQWxnPUw7Ry5lbmNyeXB0aW9uU2NoZW1lSVY9Q3J5cHRvSlMuZW5jLkhleC5zdHJpbmdpZnkocyk7cmV0dXJuIEd9O2lmKEQ9PVwiUEtDUzhQUlZcIiYmbiE9dW5kZWZpbmVkJiZiIGluc3RhbmNlb2YgbiYmYi5pc1ByaXZhdGU9PXRydWUpe3ZhciBnPUEoYik7dmFyIGQ9Zy5nZXRFbmNvZGVkSGV4KCk7dmFyIEU9bCh7c2VxOlt7XCJpbnRcIjowfSx7c2VxOlt7b2lkOntuYW1lOlwicnNhRW5jcnlwdGlvblwifX0se1wibnVsbFwiOnRydWV9XX0se29jdHN0cjp7aGV4OmR9fV19KTt2YXIgdz1FLmdldEVuY29kZWRIZXgoKTtpZih5PT09dW5kZWZpbmVkfHx5PT1udWxsKXtyZXR1cm4gaGV4dG9wZW0odyxcIlBSSVZBVEUgS0VZXCIpfWVsc2V7dmFyIHQ9byh3LHkpO3JldHVybiBoZXh0b3BlbSh0LFwiRU5DUllQVEVEIFBSSVZBVEUgS0VZXCIpfX1pZihEPT1cIlBLQ1M4UFJWXCImJnIhPT11bmRlZmluZWQmJmIgaW5zdGFuY2VvZiByJiZiLmlzUHJpdmF0ZT09dHJ1ZSl7dmFyIGc9bmV3IGwoe3NlcTpbe1wiaW50XCI6MX0se29jdHN0cjp7aGV4OmIucHJ2S2V5SGV4fX0se3RhZzpbXCJhMVwiLHRydWUse2JpdHN0cjp7aGV4OlwiMDBcIitiLnB1YktleUhleH19XX1dfSk7dmFyIGQ9Zy5nZXRFbmNvZGVkSGV4KCk7dmFyIEU9bCh7c2VxOlt7XCJpbnRcIjowfSx7c2VxOlt7b2lkOntuYW1lOlwiZWNQdWJsaWNLZXlcIn19LHtvaWQ6e25hbWU6Yi5jdXJ2ZU5hbWV9fV19LHtvY3RzdHI6e2hleDpkfX1dfSk7dmFyIHc9RS5nZXRFbmNvZGVkSGV4KCk7aWYoeT09PXVuZGVmaW5lZHx8eT09bnVsbCl7cmV0dXJuIGhleHRvcGVtKHcsXCJQUklWQVRFIEtFWVwiKX1lbHNle3ZhciB0PW8odyx5KTtyZXR1cm4gaGV4dG9wZW0odCxcIkVOQ1JZUFRFRCBQUklWQVRFIEtFWVwiKX19aWYoRD09XCJQS0NTOFBSVlwiJiZ1IT09dW5kZWZpbmVkJiZiIGluc3RhbmNlb2YgdSYmYi5pc1ByaXZhdGU9PXRydWUpe3ZhciBnPW5ldyBmKHtiaWdpbnQ6Yi54fSk7dmFyIGQ9Zy5nZXRFbmNvZGVkSGV4KCk7dmFyIEU9bCh7c2VxOlt7XCJpbnRcIjowfSx7c2VxOlt7b2lkOntuYW1lOlwiZHNhXCJ9fSx7c2VxOlt7XCJpbnRcIjp7YmlnaW50OmIucH19LHtcImludFwiOntiaWdpbnQ6Yi5xfX0se1wiaW50XCI6e2JpZ2ludDpiLmd9fV19XX0se29jdHN0cjp7aGV4OmR9fV19KTt2YXIgdz1FLmdldEVuY29kZWRIZXgoKTtpZih5PT09dW5kZWZpbmVkfHx5PT1udWxsKXtyZXR1cm4gaGV4dG9wZW0odyxcIlBSSVZBVEUgS0VZXCIpfWVsc2V7dmFyIHQ9byh3LHkpO3JldHVybiBoZXh0b3BlbSh0LFwiRU5DUllQVEVEIFBSSVZBVEUgS0VZXCIpfX10aHJvd1widW5zdXBwb3J0ZWQgb2JqZWN0IG5vciBmb3JtYXRcIn07S0VZVVRJTC5nZXRLZXlGcm9tQ1NSUEVNPWZ1bmN0aW9uKGIpe3ZhciBhPXBlbXRvaGV4KGIsXCJDRVJUSUZJQ0FURSBSRVFVRVNUXCIpO3ZhciBjPUtFWVVUSUwuZ2V0S2V5RnJvbUNTUkhleChhKTtyZXR1cm4gY307S0VZVVRJTC5nZXRLZXlGcm9tQ1NSSGV4PWZ1bmN0aW9uKGEpe3ZhciBjPUtFWVVUSUwucGFyc2VDU1JIZXgoYSk7dmFyIGI9S0VZVVRJTC5nZXRLZXkoYy5wOHB1YmtleWhleCxudWxsLFwicGtjczhwdWJcIik7cmV0dXJuIGJ9O0tFWVVUSUwucGFyc2VDU1JIZXg9ZnVuY3Rpb24oZCl7dmFyIGk9QVNOMUhFWDt2YXIgZj1pLmdldENoaWxkSWR4O3ZhciBjPWkuZ2V0VExWO3ZhciBiPXt9O3ZhciBnPWQ7aWYoZy5zdWJzdHIoMCwyKSE9XCIzMFwiKXt0aHJvd1wibWFsZm9ybWVkIENTUihjb2RlOjAwMSlcIn12YXIgZT1mKGcsMCk7aWYoZS5sZW5ndGg8MSl7dGhyb3dcIm1hbGZvcm1lZCBDU1IoY29kZTowMDIpXCJ9aWYoZy5zdWJzdHIoZVswXSwyKSE9XCIzMFwiKXt0aHJvd1wibWFsZm9ybWVkIENTUihjb2RlOjAwMylcIn12YXIgYT1mKGcsZVswXSk7aWYoYS5sZW5ndGg8Myl7dGhyb3dcIm1hbGZvcm1lZCBDU1IoY29kZTowMDQpXCJ9Yi5wOHB1YmtleWhleD1jKGcsYVsyXSk7cmV0dXJuIGJ9O0tFWVVUSUwuZ2V0SldLRnJvbUtleT1mdW5jdGlvbihkKXt2YXIgYj17fTtpZihkIGluc3RhbmNlb2YgUlNBS2V5JiZkLmlzUHJpdmF0ZSl7Yi5rdHk9XCJSU0FcIjtiLm49aGV4dG9iNjR1KGQubi50b1N0cmluZygxNikpO2IuZT1oZXh0b2I2NHUoZC5lLnRvU3RyaW5nKDE2KSk7Yi5kPWhleHRvYjY0dShkLmQudG9TdHJpbmcoMTYpKTtiLnA9aGV4dG9iNjR1KGQucC50b1N0cmluZygxNikpO2IucT1oZXh0b2I2NHUoZC5xLnRvU3RyaW5nKDE2KSk7Yi5kcD1oZXh0b2I2NHUoZC5kbXAxLnRvU3RyaW5nKDE2KSk7Yi5kcT1oZXh0b2I2NHUoZC5kbXExLnRvU3RyaW5nKDE2KSk7Yi5xaT1oZXh0b2I2NHUoZC5jb2VmZi50b1N0cmluZygxNikpO3JldHVybiBifWVsc2V7aWYoZCBpbnN0YW5jZW9mIFJTQUtleSYmZC5pc1B1YmxpYyl7Yi5rdHk9XCJSU0FcIjtiLm49aGV4dG9iNjR1KGQubi50b1N0cmluZygxNikpO2IuZT1oZXh0b2I2NHUoZC5lLnRvU3RyaW5nKDE2KSk7cmV0dXJuIGJ9ZWxzZXtpZihkIGluc3RhbmNlb2YgS0pVUi5jcnlwdG8uRUNEU0EmJmQuaXNQcml2YXRlKXt2YXIgYT1kLmdldFNob3J0TklTVFBDdXJ2ZU5hbWUoKTtpZihhIT09XCJQLTI1NlwiJiZhIT09XCJQLTM4NFwiKXt0aHJvd1widW5zdXBwb3J0ZWQgY3VydmUgbmFtZSBmb3IgSldUOiBcIithfXZhciBjPWQuZ2V0UHVibGljS2V5WFlIZXgoKTtiLmt0eT1cIkVDXCI7Yi5jcnY9YTtiLng9aGV4dG9iNjR1KGMueCk7Yi55PWhleHRvYjY0dShjLnkpO2IuZD1oZXh0b2I2NHUoZC5wcnZLZXlIZXgpO3JldHVybiBifWVsc2V7aWYoZCBpbnN0YW5jZW9mIEtKVVIuY3J5cHRvLkVDRFNBJiZkLmlzUHVibGljKXt2YXIgYT1kLmdldFNob3J0TklTVFBDdXJ2ZU5hbWUoKTtpZihhIT09XCJQLTI1NlwiJiZhIT09XCJQLTM4NFwiKXt0aHJvd1widW5zdXBwb3J0ZWQgY3VydmUgbmFtZSBmb3IgSldUOiBcIithfXZhciBjPWQuZ2V0UHVibGljS2V5WFlIZXgoKTtiLmt0eT1cIkVDXCI7Yi5jcnY9YTtiLng9aGV4dG9iNjR1KGMueCk7Yi55PWhleHRvYjY0dShjLnkpO3JldHVybiBifX19fXRocm93XCJub3Qgc3VwcG9ydGVkIGtleSBvYmplY3RcIn07XG5SU0FLZXkuZ2V0UG9zQXJyYXlPZkNoaWxkcmVuRnJvbUhleD1mdW5jdGlvbihhKXtyZXR1cm4gQVNOMUhFWC5nZXRDaGlsZElkeChhLDApfTtSU0FLZXkuZ2V0SGV4VmFsdWVBcnJheU9mQ2hpbGRyZW5Gcm9tSGV4PWZ1bmN0aW9uKGYpe3ZhciBuPUFTTjFIRVg7dmFyIGk9bi5nZXRWO3ZhciBrPVJTQUtleS5nZXRQb3NBcnJheU9mQ2hpbGRyZW5Gcm9tSGV4KGYpO3ZhciBlPWkoZixrWzBdKTt2YXIgaj1pKGYsa1sxXSk7dmFyIGI9aShmLGtbMl0pO3ZhciBjPWkoZixrWzNdKTt2YXIgaD1pKGYsa1s0XSk7dmFyIGc9aShmLGtbNV0pO3ZhciBtPWkoZixrWzZdKTt2YXIgbD1pKGYsa1s3XSk7dmFyIGQ9aShmLGtbOF0pO3ZhciBrPW5ldyBBcnJheSgpO2sucHVzaChlLGosYixjLGgsZyxtLGwsZCk7cmV0dXJuIGt9O1JTQUtleS5wcm90b3R5cGUucmVhZFByaXZhdGVLZXlGcm9tUEVNU3RyaW5nPWZ1bmN0aW9uKGQpe3ZhciBjPXBlbXRvaGV4KGQpO3ZhciBiPVJTQUtleS5nZXRIZXhWYWx1ZUFycmF5T2ZDaGlsZHJlbkZyb21IZXgoYyk7dGhpcy5zZXRQcml2YXRlRXgoYlsxXSxiWzJdLGJbM10sYls0XSxiWzVdLGJbNl0sYls3XSxiWzhdKX07UlNBS2V5LnByb3RvdHlwZS5yZWFkUEtDUzVQcnZLZXlIZXg9ZnVuY3Rpb24oYyl7dmFyIGI9UlNBS2V5LmdldEhleFZhbHVlQXJyYXlPZkNoaWxkcmVuRnJvbUhleChjKTt0aGlzLnNldFByaXZhdGVFeChiWzFdLGJbMl0sYlszXSxiWzRdLGJbNV0sYls2XSxiWzddLGJbOF0pfTtSU0FLZXkucHJvdG90eXBlLnJlYWRQS0NTOFBydktleUhleD1mdW5jdGlvbihlKXt2YXIgYyxqLGwsYixhLGYsZCxrO3ZhciBtPUFTTjFIRVg7dmFyIGc9bS5nZXRWYnlMaXN0O2lmKG0uaXNBU04xSEVYKGUpPT09ZmFsc2Upe3Rocm93XCJub3QgQVNOLjEgaGV4IHN0cmluZ1wifXRyeXtjPWcoZSwwLFsyLDAsMV0sXCIwMlwiKTtqPWcoZSwwLFsyLDAsMl0sXCIwMlwiKTtsPWcoZSwwLFsyLDAsM10sXCIwMlwiKTtiPWcoZSwwLFsyLDAsNF0sXCIwMlwiKTthPWcoZSwwLFsyLDAsNV0sXCIwMlwiKTtmPWcoZSwwLFsyLDAsNl0sXCIwMlwiKTtkPWcoZSwwLFsyLDAsN10sXCIwMlwiKTtrPWcoZSwwLFsyLDAsOF0sXCIwMlwiKX1jYXRjaChpKXt0aHJvd1wibWFsZm9ybWVkIFBLQ1MjOCBwbGFpbiBSU0EgcHJpdmF0ZSBrZXlcIn10aGlzLnNldFByaXZhdGVFeChjLGosbCxiLGEsZixkLGspfTtSU0FLZXkucHJvdG90eXBlLnJlYWRQS0NTNVB1YktleUhleD1mdW5jdGlvbihjKXt2YXIgZT1BU04xSEVYO3ZhciBiPWUuZ2V0VjtpZihlLmlzQVNOMUhFWChjKT09PWZhbHNlKXt0aHJvd1wia2V5SGV4IGlzIG5vdCBBU04uMSBoZXggc3RyaW5nXCJ9dmFyIGE9ZS5nZXRDaGlsZElkeChjLDApO2lmKGEubGVuZ3RoIT09Mnx8Yy5zdWJzdHIoYVswXSwyKSE9PVwiMDJcInx8Yy5zdWJzdHIoYVsxXSwyKSE9PVwiMDJcIil7dGhyb3dcIndyb25nIGhleCBmb3IgUEtDUyM1IHB1YmxpYyBrZXlcIn12YXIgZj1iKGMsYVswXSk7dmFyIGQ9YihjLGFbMV0pO3RoaXMuc2V0UHVibGljKGYsZCl9O1JTQUtleS5wcm90b3R5cGUucmVhZFBLQ1M4UHViS2V5SGV4PWZ1bmN0aW9uKGIpe3ZhciBjPUFTTjFIRVg7aWYoYy5pc0FTTjFIRVgoYik9PT1mYWxzZSl7dGhyb3dcIm5vdCBBU04uMSBoZXggc3RyaW5nXCJ9aWYoYy5nZXRUTFZieUxpc3QoYiwwLFswLDBdKSE9PVwiMDYwOTJhODY0ODg2ZjcwZDAxMDEwMVwiKXt0aHJvd1wibm90IFBLQ1M4IFJTQSBwdWJsaWMga2V5XCJ9dmFyIGE9Yy5nZXRUTFZieUxpc3QoYiwwLFsxLDBdKTt0aGlzLnJlYWRQS0NTNVB1YktleUhleChhKX07UlNBS2V5LnByb3RvdHlwZS5yZWFkQ2VydFB1YktleUhleD1mdW5jdGlvbihiLGQpe3ZhciBhLGM7YT1uZXcgWDUwOSgpO2EucmVhZENlcnRIZXgoYik7Yz1hLmdldFB1YmxpY0tleUhleCgpO3RoaXMucmVhZFBLQ1M4UHViS2V5SGV4KGMpfTtcbnZhciBfUkVfSEVYREVDT05MWT1uZXcgUmVnRXhwKFwiXCIpO19SRV9IRVhERUNPTkxZLmNvbXBpbGUoXCJbXjAtOWEtZl1cIixcImdpXCIpO2Z1bmN0aW9uIF9yc2FzaWduX2dldEhleFBhZGRlZERpZ2VzdEluZm9Gb3JTdHJpbmcoZCxlLGEpe3ZhciBiPWZ1bmN0aW9uKGYpe3JldHVybiBLSlVSLmNyeXB0by5VdGlsLmhhc2hTdHJpbmcoZixhKX07dmFyIGM9YihkKTtyZXR1cm4gS0pVUi5jcnlwdG8uVXRpbC5nZXRQYWRkZWREaWdlc3RJbmZvSGV4KGMsYSxlKX1mdW5jdGlvbiBfemVyb1BhZGRpbmdPZlNpZ25hdHVyZShlLGQpe3ZhciBjPVwiXCI7dmFyIGE9ZC80LWUubGVuZ3RoO2Zvcih2YXIgYj0wO2I8YTtiKyspe2M9YytcIjBcIn1yZXR1cm4gYytlfVJTQUtleS5wcm90b3R5cGUuc2lnbj1mdW5jdGlvbihkLGEpe3ZhciBiPWZ1bmN0aW9uKGUpe3JldHVybiBLSlVSLmNyeXB0by5VdGlsLmhhc2hTdHJpbmcoZSxhKX07dmFyIGM9YihkKTtyZXR1cm4gdGhpcy5zaWduV2l0aE1lc3NhZ2VIYXNoKGMsYSl9O1JTQUtleS5wcm90b3R5cGUuc2lnbldpdGhNZXNzYWdlSGFzaD1mdW5jdGlvbihlLGMpe3ZhciBmPUtKVVIuY3J5cHRvLlV0aWwuZ2V0UGFkZGVkRGlnZXN0SW5mb0hleChlLGMsdGhpcy5uLmJpdExlbmd0aCgpKTt2YXIgYj1wYXJzZUJpZ0ludChmLDE2KTt2YXIgZD10aGlzLmRvUHJpdmF0ZShiKTt2YXIgYT1kLnRvU3RyaW5nKDE2KTtyZXR1cm4gX3plcm9QYWRkaW5nT2ZTaWduYXR1cmUoYSx0aGlzLm4uYml0TGVuZ3RoKCkpfTtmdW5jdGlvbiBwc3NfbWdmMV9zdHIoYyxhLGUpe3ZhciBiPVwiXCIsZD0wO3doaWxlKGIubGVuZ3RoPGEpe2IrPWhleHRvcnN0cihlKHJzdHJ0b2hleChjK1N0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkoU3RyaW5nLFsoZCY0Mjc4MTkwMDgwKT4+MjQsKGQmMTY3MTE2ODApPj4xNiwoZCY2NTI4MCk+PjgsZCYyNTVdKSkpKTtkKz0xfXJldHVybiBifVJTQUtleS5wcm90b3R5cGUuc2lnblBTUz1mdW5jdGlvbihlLGEsZCl7dmFyIGM9ZnVuY3Rpb24oZil7cmV0dXJuIEtKVVIuY3J5cHRvLlV0aWwuaGFzaEhleChmLGEpfTt2YXIgYj1jKHJzdHJ0b2hleChlKSk7aWYoZD09PXVuZGVmaW5lZCl7ZD0tMX1yZXR1cm4gdGhpcy5zaWduV2l0aE1lc3NhZ2VIYXNoUFNTKGIsYSxkKX07UlNBS2V5LnByb3RvdHlwZS5zaWduV2l0aE1lc3NhZ2VIYXNoUFNTPWZ1bmN0aW9uKGwsYSxrKXt2YXIgYj1oZXh0b3JzdHIobCk7dmFyIGc9Yi5sZW5ndGg7dmFyIG09dGhpcy5uLmJpdExlbmd0aCgpLTE7dmFyIGM9TWF0aC5jZWlsKG0vOCk7dmFyIGQ7dmFyIG89ZnVuY3Rpb24oaSl7cmV0dXJuIEtKVVIuY3J5cHRvLlV0aWwuaGFzaEhleChpLGEpfTtpZihrPT09LTF8fGs9PT11bmRlZmluZWQpe2s9Z31lbHNle2lmKGs9PT0tMil7az1jLWctMn1lbHNle2lmKGs8LTIpe3Rocm93XCJpbnZhbGlkIHNhbHQgbGVuZ3RoXCJ9fX1pZihjPChnK2srMikpe3Rocm93XCJkYXRhIHRvbyBsb25nXCJ9dmFyIGY9XCJcIjtpZihrPjApe2Y9bmV3IEFycmF5KGspO25ldyBTZWN1cmVSYW5kb20oKS5uZXh0Qnl0ZXMoZik7Zj1TdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KFN0cmluZyxmKX12YXIgbj1oZXh0b3JzdHIobyhyc3RydG9oZXgoXCJcXHgwMFxceDAwXFx4MDBcXHgwMFxceDAwXFx4MDBcXHgwMFxceDAwXCIrYitmKSkpO3ZhciBqPVtdO2ZvcihkPTA7ZDxjLWstZy0yO2QrPTEpe2pbZF09MH12YXIgZT1TdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KFN0cmluZyxqKStcIlxceDAxXCIrZjt2YXIgaD1wc3NfbWdmMV9zdHIobixlLmxlbmd0aCxvKTt2YXIgcT1bXTtmb3IoZD0wO2Q8ZS5sZW5ndGg7ZCs9MSl7cVtkXT1lLmNoYXJDb2RlQXQoZCleaC5jaGFyQ29kZUF0KGQpfXZhciBwPSg2NTI4MD4+KDgqYy1tKSkmMjU1O3FbMF0mPX5wO2ZvcihkPTA7ZDxnO2QrKyl7cS5wdXNoKG4uY2hhckNvZGVBdChkKSl9cS5wdXNoKDE4OCk7cmV0dXJuIF96ZXJvUGFkZGluZ09mU2lnbmF0dXJlKHRoaXMuZG9Qcml2YXRlKG5ldyBCaWdJbnRlZ2VyKHEpKS50b1N0cmluZygxNiksdGhpcy5uLmJpdExlbmd0aCgpKX07ZnVuY3Rpb24gX3JzYXNpZ25fZ2V0RGVjcnlwdFNpZ25hdHVyZUJJKGEsZCxjKXt2YXIgYj1uZXcgUlNBS2V5KCk7Yi5zZXRQdWJsaWMoZCxjKTt2YXIgZT1iLmRvUHVibGljKGEpO3JldHVybiBlfWZ1bmN0aW9uIF9yc2FzaWduX2dldEhleERpZ2VzdEluZm9Gcm9tU2lnKGEsYyxiKXt2YXIgZT1fcnNhc2lnbl9nZXREZWNyeXB0U2lnbmF0dXJlQkkoYSxjLGIpO3ZhciBkPWUudG9TdHJpbmcoMTYpLnJlcGxhY2UoL14xZiswMC8sXCJcIik7cmV0dXJuIGR9ZnVuY3Rpb24gX3JzYXNpZ25fZ2V0QWxnTmFtZUFuZEhhc2hGcm9tSGV4RGlzZ2VzdEluZm8oZil7Zm9yKHZhciBlIGluIEtKVVIuY3J5cHRvLlV0aWwuRElHRVNUSU5GT0hFQUQpe3ZhciBkPUtKVVIuY3J5cHRvLlV0aWwuRElHRVNUSU5GT0hFQURbZV07dmFyIGI9ZC5sZW5ndGg7aWYoZi5zdWJzdHJpbmcoMCxiKT09ZCl7dmFyIGM9W2UsZi5zdWJzdHJpbmcoYildO3JldHVybiBjfX1yZXR1cm5bXX1SU0FLZXkucHJvdG90eXBlLnZlcmlmeT1mdW5jdGlvbihmLGope2o9ai5yZXBsYWNlKF9SRV9IRVhERUNPTkxZLFwiXCIpO2o9ai5yZXBsYWNlKC9bIFxcbl0rL2csXCJcIik7dmFyIGI9cGFyc2VCaWdJbnQoaiwxNik7aWYoYi5iaXRMZW5ndGgoKT50aGlzLm4uYml0TGVuZ3RoKCkpe3JldHVybiAwfXZhciBpPXRoaXMuZG9QdWJsaWMoYik7dmFyIGU9aS50b1N0cmluZygxNikucmVwbGFjZSgvXjFmKzAwLyxcIlwiKTt2YXIgZz1fcnNhc2lnbl9nZXRBbGdOYW1lQW5kSGFzaEZyb21IZXhEaXNnZXN0SW5mbyhlKTtpZihnLmxlbmd0aD09MCl7cmV0dXJuIGZhbHNlfXZhciBkPWdbMF07dmFyIGg9Z1sxXTt2YXIgYT1mdW5jdGlvbihrKXtyZXR1cm4gS0pVUi5jcnlwdG8uVXRpbC5oYXNoU3RyaW5nKGssZCl9O3ZhciBjPWEoZik7cmV0dXJuKGg9PWMpfTtSU0FLZXkucHJvdG90eXBlLnZlcmlmeVdpdGhNZXNzYWdlSGFzaD1mdW5jdGlvbihlLGEpe2E9YS5yZXBsYWNlKF9SRV9IRVhERUNPTkxZLFwiXCIpO2E9YS5yZXBsYWNlKC9bIFxcbl0rL2csXCJcIik7dmFyIGI9cGFyc2VCaWdJbnQoYSwxNik7aWYoYi5iaXRMZW5ndGgoKT50aGlzLm4uYml0TGVuZ3RoKCkpe3JldHVybiAwfXZhciBoPXRoaXMuZG9QdWJsaWMoYik7dmFyIGc9aC50b1N0cmluZygxNikucmVwbGFjZSgvXjFmKzAwLyxcIlwiKTt2YXIgYz1fcnNhc2lnbl9nZXRBbGdOYW1lQW5kSGFzaEZyb21IZXhEaXNnZXN0SW5mbyhnKTtpZihjLmxlbmd0aD09MCl7cmV0dXJuIGZhbHNlfXZhciBkPWNbMF07dmFyIGY9Y1sxXTtyZXR1cm4oZj09ZSl9O1JTQUtleS5wcm90b3R5cGUudmVyaWZ5UFNTPWZ1bmN0aW9uKGMsYixhLGYpe3ZhciBlPWZ1bmN0aW9uKGcpe3JldHVybiBLSlVSLmNyeXB0by5VdGlsLmhhc2hIZXgoZyxhKX07dmFyIGQ9ZShyc3RydG9oZXgoYykpO2lmKGY9PT11bmRlZmluZWQpe2Y9LTF9cmV0dXJuIHRoaXMudmVyaWZ5V2l0aE1lc3NhZ2VIYXNoUFNTKGQsYixhLGYpfTtSU0FLZXkucHJvdG90eXBlLnZlcmlmeVdpdGhNZXNzYWdlSGFzaFBTUz1mdW5jdGlvbihmLHMsbCxjKXt2YXIgaz1uZXcgQmlnSW50ZWdlcihzLDE2KTtpZihrLmJpdExlbmd0aCgpPnRoaXMubi5iaXRMZW5ndGgoKSl7cmV0dXJuIGZhbHNlfXZhciByPWZ1bmN0aW9uKGkpe3JldHVybiBLSlVSLmNyeXB0by5VdGlsLmhhc2hIZXgoaSxsKX07dmFyIGo9aGV4dG9yc3RyKGYpO3ZhciBoPWoubGVuZ3RoO3ZhciBnPXRoaXMubi5iaXRMZW5ndGgoKS0xO3ZhciBtPU1hdGguY2VpbChnLzgpO3ZhciBxO2lmKGM9PT0tMXx8Yz09PXVuZGVmaW5lZCl7Yz1ofWVsc2V7aWYoYz09PS0yKXtjPW0taC0yfWVsc2V7aWYoYzwtMil7dGhyb3dcImludmFsaWQgc2FsdCBsZW5ndGhcIn19fWlmKG08KGgrYysyKSl7dGhyb3dcImRhdGEgdG9vIGxvbmdcIn12YXIgYT10aGlzLmRvUHVibGljKGspLnRvQnl0ZUFycmF5KCk7Zm9yKHE9MDtxPGEubGVuZ3RoO3ErPTEpe2FbcV0mPTI1NX13aGlsZShhLmxlbmd0aDxtKXthLnVuc2hpZnQoMCl9aWYoYVttLTFdIT09MTg4KXt0aHJvd1wiZW5jb2RlZCBtZXNzYWdlIGRvZXMgbm90IGVuZCBpbiAweGJjXCJ9YT1TdHJpbmcuZnJvbUNoYXJDb2RlLmFwcGx5KFN0cmluZyxhKTt2YXIgZD1hLnN1YnN0cigwLG0taC0xKTt2YXIgZT1hLnN1YnN0cihkLmxlbmd0aCxoKTt2YXIgcD0oNjUyODA+Pig4Km0tZykpJjI1NTtpZigoZC5jaGFyQ29kZUF0KDApJnApIT09MCl7dGhyb3dcImJpdHMgYmV5b25kIGtleXNpemUgbm90IHplcm9cIn12YXIgbj1wc3NfbWdmMV9zdHIoZSxkLmxlbmd0aCxyKTt2YXIgbz1bXTtmb3IocT0wO3E8ZC5sZW5ndGg7cSs9MSl7b1txXT1kLmNoYXJDb2RlQXQocSlebi5jaGFyQ29kZUF0KHEpfW9bMF0mPX5wO3ZhciBiPW0taC1jLTI7Zm9yKHE9MDtxPGI7cSs9MSl7aWYob1txXSE9PTApe3Rocm93XCJsZWZ0bW9zdCBvY3RldHMgbm90IHplcm9cIn19aWYob1tiXSE9PTEpe3Rocm93XCIweDAxIG1hcmtlciBub3QgZm91bmRcIn1yZXR1cm4gZT09PWhleHRvcnN0cihyKHJzdHJ0b2hleChcIlxceDAwXFx4MDBcXHgwMFxceDAwXFx4MDBcXHgwMFxceDAwXFx4MDBcIitqK1N0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkoU3RyaW5nLG8uc2xpY2UoLWMpKSkpKX07UlNBS2V5LlNBTFRfTEVOX0hMRU49LTE7UlNBS2V5LlNBTFRfTEVOX01BWD0tMjtSU0FLZXkuU0FMVF9MRU5fUkVDT1ZFUj0tMjtcbmZ1bmN0aW9uIFg1MDkoKXt2YXIgaz1BU04xSEVYLGo9ay5nZXRDaGlsZElkeCxoPWsuZ2V0VixiPWsuZ2V0VExWLGY9ay5nZXRWYnlMaXN0LGM9ay5nZXRUTFZieUxpc3QsZz1rLmdldElkeGJ5TGlzdCxkPWsuZ2V0VmlkeCxpPWsub2lkbmFtZSxhPVg1MDksZT1wZW10b2hleDt0aGlzLmhleD1udWxsO3RoaXMudmVyc2lvbj0wO3RoaXMuZm9mZnNldD0wO3RoaXMuYUV4dEluZm89bnVsbDt0aGlzLmdldFZlcnNpb249ZnVuY3Rpb24oKXtpZih0aGlzLmhleD09PW51bGx8fHRoaXMudmVyc2lvbiE9PTApe3JldHVybiB0aGlzLnZlcnNpb259aWYoYyh0aGlzLmhleCwwLFswLDBdKSE9PVwiYTAwMzAyMDEwMlwiKXt0aGlzLnZlcnNpb249MTt0aGlzLmZvZmZzZXQ9LTE7cmV0dXJuIDF9dGhpcy52ZXJzaW9uPTM7cmV0dXJuIDN9O3RoaXMuZ2V0U2VyaWFsTnVtYmVySGV4PWZ1bmN0aW9uKCl7cmV0dXJuIGYodGhpcy5oZXgsMCxbMCwxK3RoaXMuZm9mZnNldF0sXCIwMlwiKX07dGhpcy5nZXRTaWduYXR1cmVBbGdvcml0aG1GaWVsZD1mdW5jdGlvbigpe3JldHVybiBpKGYodGhpcy5oZXgsMCxbMCwyK3RoaXMuZm9mZnNldCwwXSxcIjA2XCIpKX07dGhpcy5nZXRJc3N1ZXJIZXg9ZnVuY3Rpb24oKXtyZXR1cm4gYyh0aGlzLmhleCwwLFswLDMrdGhpcy5mb2Zmc2V0XSxcIjMwXCIpfTt0aGlzLmdldElzc3VlclN0cmluZz1mdW5jdGlvbigpe3JldHVybiBhLmhleDJkbih0aGlzLmdldElzc3VlckhleCgpKX07dGhpcy5nZXRTdWJqZWN0SGV4PWZ1bmN0aW9uKCl7cmV0dXJuIGModGhpcy5oZXgsMCxbMCw1K3RoaXMuZm9mZnNldF0sXCIzMFwiKX07dGhpcy5nZXRTdWJqZWN0U3RyaW5nPWZ1bmN0aW9uKCl7cmV0dXJuIGEuaGV4MmRuKHRoaXMuZ2V0U3ViamVjdEhleCgpKX07dGhpcy5nZXROb3RCZWZvcmU9ZnVuY3Rpb24oKXt2YXIgbD1mKHRoaXMuaGV4LDAsWzAsNCt0aGlzLmZvZmZzZXQsMF0pO2w9bC5yZXBsYWNlKC8oLi4pL2csXCIlJDFcIik7bD1kZWNvZGVVUklDb21wb25lbnQobCk7cmV0dXJuIGx9O3RoaXMuZ2V0Tm90QWZ0ZXI9ZnVuY3Rpb24oKXt2YXIgbD1mKHRoaXMuaGV4LDAsWzAsNCt0aGlzLmZvZmZzZXQsMV0pO2w9bC5yZXBsYWNlKC8oLi4pL2csXCIlJDFcIik7bD1kZWNvZGVVUklDb21wb25lbnQobCk7cmV0dXJuIGx9O3RoaXMuZ2V0UHVibGljS2V5SGV4PWZ1bmN0aW9uKCl7cmV0dXJuIGsuZ2V0VExWYnlMaXN0KHRoaXMuaGV4LDAsWzAsNit0aGlzLmZvZmZzZXRdLFwiMzBcIil9O3RoaXMuZ2V0UHVibGljS2V5SWR4PWZ1bmN0aW9uKCl7cmV0dXJuIGcodGhpcy5oZXgsMCxbMCw2K3RoaXMuZm9mZnNldF0sXCIzMFwiKX07dGhpcy5nZXRQdWJsaWNLZXlDb250ZW50SWR4PWZ1bmN0aW9uKCl7dmFyIGw9dGhpcy5nZXRQdWJsaWNLZXlJZHgoKTtyZXR1cm4gZyh0aGlzLmhleCxsLFsxLDBdLFwiMzBcIil9O3RoaXMuZ2V0UHVibGljS2V5PWZ1bmN0aW9uKCl7cmV0dXJuIEtFWVVUSUwuZ2V0S2V5KHRoaXMuZ2V0UHVibGljS2V5SGV4KCksbnVsbCxcInBrY3M4cHViXCIpfTt0aGlzLmdldFNpZ25hdHVyZUFsZ29yaXRobU5hbWU9ZnVuY3Rpb24oKXtyZXR1cm4gaShmKHRoaXMuaGV4LDAsWzEsMF0sXCIwNlwiKSl9O3RoaXMuZ2V0U2lnbmF0dXJlVmFsdWVIZXg9ZnVuY3Rpb24oKXtyZXR1cm4gZih0aGlzLmhleCwwLFsyXSxcIjAzXCIsdHJ1ZSl9O3RoaXMudmVyaWZ5U2lnbmF0dXJlPWZ1bmN0aW9uKG4pe3ZhciBvPXRoaXMuZ2V0U2lnbmF0dXJlQWxnb3JpdGhtTmFtZSgpO3ZhciBsPXRoaXMuZ2V0U2lnbmF0dXJlVmFsdWVIZXgoKTt2YXIgbT1jKHRoaXMuaGV4LDAsWzBdLFwiMzBcIik7dmFyIHA9bmV3IEtKVVIuY3J5cHRvLlNpZ25hdHVyZSh7YWxnOm99KTtwLmluaXQobik7cC51cGRhdGVIZXgobSk7cmV0dXJuIHAudmVyaWZ5KGwpfTt0aGlzLnBhcnNlRXh0PWZ1bmN0aW9uKCl7aWYodGhpcy52ZXJzaW9uIT09Myl7cmV0dXJuIC0xfXZhciBwPWcodGhpcy5oZXgsMCxbMCw3LDBdLFwiMzBcIik7dmFyIG09aih0aGlzLmhleCxwKTt0aGlzLmFFeHRJbmZvPW5ldyBBcnJheSgpO2Zvcih2YXIgbj0wO248bS5sZW5ndGg7bisrKXt2YXIgcT17fTtxLmNyaXRpY2FsPWZhbHNlO3ZhciBsPWoodGhpcy5oZXgsbVtuXSk7dmFyIHI9MDtpZihsLmxlbmd0aD09PTMpe3EuY3JpdGljYWw9dHJ1ZTtyPTF9cS5vaWQ9ay5oZXh0b29pZHN0cihmKHRoaXMuaGV4LG1bbl0sWzBdLFwiMDZcIikpO3ZhciBvPWcodGhpcy5oZXgsbVtuXSxbMStyXSk7cS52aWR4PWQodGhpcy5oZXgsbyk7dGhpcy5hRXh0SW5mby5wdXNoKHEpfX07dGhpcy5nZXRFeHRJbmZvPWZ1bmN0aW9uKG4pe3ZhciBsPXRoaXMuYUV4dEluZm87dmFyIG89bjtpZighbi5tYXRjaCgvXlswLTkuXSskLykpe289S0pVUi5hc24xLng1MDkuT0lELm5hbWUyb2lkKG4pfWlmKG89PT1cIlwiKXtyZXR1cm4gdW5kZWZpbmVkfWZvcih2YXIgbT0wO208bC5sZW5ndGg7bSsrKXtpZihsW21dLm9pZD09PW8pe3JldHVybiBsW21dfX1yZXR1cm4gdW5kZWZpbmVkfTt0aGlzLmdldEV4dEJhc2ljQ29uc3RyYWludHM9ZnVuY3Rpb24oKXt2YXIgbj10aGlzLmdldEV4dEluZm8oXCJiYXNpY0NvbnN0cmFpbnRzXCIpO2lmKG49PT11bmRlZmluZWQpe3JldHVybiBufXZhciBsPWgodGhpcy5oZXgsbi52aWR4KTtpZihsPT09XCJcIil7cmV0dXJue319aWYobD09PVwiMDEwMWZmXCIpe3JldHVybntjQTp0cnVlfX1pZihsLnN1YnN0cigwLDgpPT09XCIwMTAxZmYwMlwiKXt2YXIgbz1oKGwsNik7dmFyIG09cGFyc2VJbnQobywxNik7cmV0dXJue2NBOnRydWUscGF0aExlbjptfX10aHJvd1wiYmFzaWNDb25zdHJhaW50cyBwYXJzZSBlcnJvclwifTt0aGlzLmdldEV4dEtleVVzYWdlQmluPWZ1bmN0aW9uKCl7dmFyIG89dGhpcy5nZXRFeHRJbmZvKFwia2V5VXNhZ2VcIik7aWYobz09PXVuZGVmaW5lZCl7cmV0dXJuXCJcIn12YXIgbT1oKHRoaXMuaGV4LG8udmlkeCk7aWYobS5sZW5ndGglMiE9MHx8bS5sZW5ndGg8PTIpe3Rocm93XCJtYWxmb3JtZWQga2V5IHVzYWdlIHZhbHVlXCJ9dmFyIGw9cGFyc2VJbnQobS5zdWJzdHIoMCwyKSk7dmFyIG49cGFyc2VJbnQobS5zdWJzdHIoMiksMTYpLnRvU3RyaW5nKDIpO3JldHVybiBuLnN1YnN0cigwLG4ubGVuZ3RoLWwpfTt0aGlzLmdldEV4dEtleVVzYWdlU3RyaW5nPWZ1bmN0aW9uKCl7dmFyIG49dGhpcy5nZXRFeHRLZXlVc2FnZUJpbigpO3ZhciBsPW5ldyBBcnJheSgpO2Zvcih2YXIgbT0wO208bi5sZW5ndGg7bSsrKXtpZihuLnN1YnN0cihtLDEpPT1cIjFcIil7bC5wdXNoKFg1MDkuS0VZVVNBR0VfTkFNRVttXSl9fXJldHVybiBsLmpvaW4oXCIsXCIpfTt0aGlzLmdldEV4dFN1YmplY3RLZXlJZGVudGlmaWVyPWZ1bmN0aW9uKCl7dmFyIGw9dGhpcy5nZXRFeHRJbmZvKFwic3ViamVjdEtleUlkZW50aWZpZXJcIik7aWYobD09PXVuZGVmaW5lZCl7cmV0dXJuIGx9cmV0dXJuIGgodGhpcy5oZXgsbC52aWR4KX07dGhpcy5nZXRFeHRBdXRob3JpdHlLZXlJZGVudGlmaWVyPWZ1bmN0aW9uKCl7dmFyIHA9dGhpcy5nZXRFeHRJbmZvKFwiYXV0aG9yaXR5S2V5SWRlbnRpZmllclwiKTtpZihwPT09dW5kZWZpbmVkKXtyZXR1cm4gcH12YXIgbD17fTt2YXIgbz1iKHRoaXMuaGV4LHAudmlkeCk7dmFyIG09aihvLDApO2Zvcih2YXIgbj0wO248bS5sZW5ndGg7bisrKXtpZihvLnN1YnN0cihtW25dLDIpPT09XCI4MFwiKXtsLmtpZD1oKG8sbVtuXSl9fXJldHVybiBsfTt0aGlzLmdldEV4dEV4dEtleVVzYWdlTmFtZT1mdW5jdGlvbigpe3ZhciBwPXRoaXMuZ2V0RXh0SW5mbyhcImV4dEtleVVzYWdlXCIpO2lmKHA9PT11bmRlZmluZWQpe3JldHVybiBwfXZhciBsPW5ldyBBcnJheSgpO3ZhciBvPWIodGhpcy5oZXgscC52aWR4KTtpZihvPT09XCJcIil7cmV0dXJuIGx9dmFyIG09aihvLDApO2Zvcih2YXIgbj0wO248bS5sZW5ndGg7bisrKXtsLnB1c2goaShoKG8sbVtuXSkpKX1yZXR1cm4gbH07dGhpcy5nZXRFeHRTdWJqZWN0QWx0TmFtZT1mdW5jdGlvbigpe3ZhciBtPXRoaXMuZ2V0RXh0U3ViamVjdEFsdE5hbWUyKCk7dmFyIGw9bmV3IEFycmF5KCk7Zm9yKHZhciBuPTA7bjxtLmxlbmd0aDtuKyspe2lmKG1bbl1bMF09PT1cIkROU1wiKXtsLnB1c2gobVtuXVsxXSl9fXJldHVybiBsfTt0aGlzLmdldEV4dFN1YmplY3RBbHROYW1lMj1mdW5jdGlvbigpe3ZhciBwLHMscjt2YXIgcT10aGlzLmdldEV4dEluZm8oXCJzdWJqZWN0QWx0TmFtZVwiKTtpZihxPT09dW5kZWZpbmVkKXtyZXR1cm4gcX12YXIgbD1uZXcgQXJyYXkoKTt2YXIgbz1iKHRoaXMuaGV4LHEudmlkeCk7dmFyIG09aihvLDApO2Zvcih2YXIgbj0wO248bS5sZW5ndGg7bisrKXtyPW8uc3Vic3RyKG1bbl0sMik7cD1oKG8sbVtuXSk7aWYocj09PVwiODFcIil7cz1oZXh0b3V0ZjgocCk7bC5wdXNoKFtcIk1BSUxcIixzXSl9aWYocj09PVwiODJcIil7cz1oZXh0b3V0ZjgocCk7bC5wdXNoKFtcIkROU1wiLHNdKX1pZihyPT09XCI4NFwiKXtzPVg1MDkuaGV4MmRuKHAsMCk7bC5wdXNoKFtcIkROXCIsc10pfWlmKHI9PT1cIjg2XCIpe3M9aGV4dG91dGY4KHApO2wucHVzaChbXCJVUklcIixzXSl9aWYocj09PVwiODdcIil7cz1oZXh0b2lwKHApO2wucHVzaChbXCJJUFwiLHNdKX19cmV0dXJuIGx9O3RoaXMuZ2V0RXh0Q1JMRGlzdHJpYnV0aW9uUG9pbnRzVVJJPWZ1bmN0aW9uKCl7dmFyIHE9dGhpcy5nZXRFeHRJbmZvKFwiY1JMRGlzdHJpYnV0aW9uUG9pbnRzXCIpO2lmKHE9PT11bmRlZmluZWQpe3JldHVybiBxfXZhciBsPW5ldyBBcnJheSgpO3ZhciBtPWoodGhpcy5oZXgscS52aWR4KTtmb3IodmFyIG89MDtvPG0ubGVuZ3RoO28rKyl7dHJ5e3ZhciByPWYodGhpcy5oZXgsbVtvXSxbMCwwLDBdLFwiODZcIik7dmFyIHA9aGV4dG91dGY4KHIpO2wucHVzaChwKX1jYXRjaChuKXt9fXJldHVybiBsfTt0aGlzLmdldEV4dEFJQUluZm89ZnVuY3Rpb24oKXt2YXIgcD10aGlzLmdldEV4dEluZm8oXCJhdXRob3JpdHlJbmZvQWNjZXNzXCIpO2lmKHA9PT11bmRlZmluZWQpe3JldHVybiBwfXZhciBsPXtvY3NwOltdLGNhaXNzdWVyOltdfTt2YXIgbT1qKHRoaXMuaGV4LHAudmlkeCk7Zm9yKHZhciBuPTA7bjxtLmxlbmd0aDtuKyspe3ZhciBxPWYodGhpcy5oZXgsbVtuXSxbMF0sXCIwNlwiKTt2YXIgbz1mKHRoaXMuaGV4LG1bbl0sWzFdLFwiODZcIik7aWYocT09PVwiMmIwNjAxMDUwNTA3MzAwMVwiKXtsLm9jc3AucHVzaChoZXh0b3V0ZjgobykpfWlmKHE9PT1cIjJiMDYwMTA1MDUwNzMwMDJcIil7bC5jYWlzc3Vlci5wdXNoKGhleHRvdXRmOChvKSl9fXJldHVybiBsfTt0aGlzLmdldEV4dENlcnRpZmljYXRlUG9saWNpZXM9ZnVuY3Rpb24oKXt2YXIgbz10aGlzLmdldEV4dEluZm8oXCJjZXJ0aWZpY2F0ZVBvbGljaWVzXCIpO2lmKG89PT11bmRlZmluZWQpe3JldHVybiBvfXZhciBsPWIodGhpcy5oZXgsby52aWR4KTt2YXIgdT1bXTt2YXIgcz1qKGwsMCk7Zm9yKHZhciByPTA7cjxzLmxlbmd0aDtyKyspe3ZhciB0PXt9O3ZhciBuPWoobCxzW3JdKTt0LmlkPWkoaChsLG5bMF0pKTtpZihuLmxlbmd0aD09PTIpe3ZhciBtPWoobCxuWzFdKTtmb3IodmFyIHE9MDtxPG0ubGVuZ3RoO3ErKyl7dmFyIHA9ZihsLG1bcV0sWzBdLFwiMDZcIik7aWYocD09PVwiMmIwNjAxMDUwNTA3MDIwMVwiKXt0LmNwcz1oZXh0b3V0ZjgoZihsLG1bcV0sWzFdKSl9ZWxzZXtpZihwPT09XCIyYjA2MDEwNTA1MDcwMjAyXCIpe3QudW5vdGljZT1oZXh0b3V0ZjgoZihsLG1bcV0sWzEsMF0pKX19fX11LnB1c2godCl9cmV0dXJuIHV9O3RoaXMucmVhZENlcnRQRU09ZnVuY3Rpb24obCl7dGhpcy5yZWFkQ2VydEhleChlKGwpKX07dGhpcy5yZWFkQ2VydEhleD1mdW5jdGlvbihsKXt0aGlzLmhleD1sO3RoaXMuZ2V0VmVyc2lvbigpO3RyeXtnKHRoaXMuaGV4LDAsWzAsN10sXCJhM1wiKTt0aGlzLnBhcnNlRXh0KCl9Y2F0Y2gobSl7fX07dGhpcy5nZXRJbmZvPWZ1bmN0aW9uKCl7dmFyIG09WDUwOTt2YXIgQix1LHo7Qj1cIkJhc2ljIEZpZWxkc1xcblwiO0IrPVwiICBzZXJpYWwgbnVtYmVyOiBcIit0aGlzLmdldFNlcmlhbE51bWJlckhleCgpK1wiXFxuXCI7Qis9XCIgIHNpZ25hdHVyZSBhbGdvcml0aG06IFwiK3RoaXMuZ2V0U2lnbmF0dXJlQWxnb3JpdGhtRmllbGQoKStcIlxcblwiO0IrPVwiICBpc3N1ZXI6IFwiK3RoaXMuZ2V0SXNzdWVyU3RyaW5nKCkrXCJcXG5cIjtCKz1cIiAgbm90QmVmb3JlOiBcIit0aGlzLmdldE5vdEJlZm9yZSgpK1wiXFxuXCI7Qis9XCIgIG5vdEFmdGVyOiBcIit0aGlzLmdldE5vdEFmdGVyKCkrXCJcXG5cIjtCKz1cIiAgc3ViamVjdDogXCIrdGhpcy5nZXRTdWJqZWN0U3RyaW5nKCkrXCJcXG5cIjtCKz1cIiAgc3ViamVjdCBwdWJsaWMga2V5IGluZm86IFxcblwiO3U9dGhpcy5nZXRQdWJsaWNLZXkoKTtCKz1cIiAgICBrZXkgYWxnb3JpdGhtOiBcIit1LnR5cGUrXCJcXG5cIjtpZih1LnR5cGU9PT1cIlJTQVwiKXtCKz1cIiAgICBuPVwiK2hleHRvcG9zaGV4KHUubi50b1N0cmluZygxNikpLnN1YnN0cigwLDE2KStcIi4uLlxcblwiO0IrPVwiICAgIGU9XCIraGV4dG9wb3NoZXgodS5lLnRvU3RyaW5nKDE2KSkrXCJcXG5cIn16PXRoaXMuYUV4dEluZm87aWYoeiE9PXVuZGVmaW5lZCYmeiE9PW51bGwpe0IrPVwiWDUwOXYzIEV4dGVuc2lvbnM6XFxuXCI7Zm9yKHZhciByPTA7cjx6Lmxlbmd0aDtyKyspe3ZhciBuPXpbcl07dmFyIEE9S0pVUi5hc24xLng1MDkuT0lELm9pZDJuYW1lKG4ub2lkKTtpZihBPT09XCJcIil7QT1uLm9pZH12YXIgeD1cIlwiO2lmKG4uY3JpdGljYWw9PT10cnVlKXt4PVwiQ1JJVElDQUxcIn1CKz1cIiAgXCIrQStcIiBcIit4K1wiOlxcblwiO2lmKEE9PT1cImJhc2ljQ29uc3RyYWludHNcIil7dmFyIHY9dGhpcy5nZXRFeHRCYXNpY0NvbnN0cmFpbnRzKCk7aWYodi5jQT09PXVuZGVmaW5lZCl7Qis9XCIgICAge31cXG5cIn1lbHNle0IrPVwiICAgIGNBPXRydWVcIjtpZih2LnBhdGhMZW4hPT11bmRlZmluZWQpe0IrPVwiLCBwYXRoTGVuPVwiK3YucGF0aExlbn1CKz1cIlxcblwifX1lbHNle2lmKEE9PT1cImtleVVzYWdlXCIpe0IrPVwiICAgIFwiK3RoaXMuZ2V0RXh0S2V5VXNhZ2VTdHJpbmcoKStcIlxcblwifWVsc2V7aWYoQT09PVwic3ViamVjdEtleUlkZW50aWZpZXJcIil7Qis9XCIgICAgXCIrdGhpcy5nZXRFeHRTdWJqZWN0S2V5SWRlbnRpZmllcigpK1wiXFxuXCJ9ZWxzZXtpZihBPT09XCJhdXRob3JpdHlLZXlJZGVudGlmaWVyXCIpe3ZhciBsPXRoaXMuZ2V0RXh0QXV0aG9yaXR5S2V5SWRlbnRpZmllcigpO2lmKGwua2lkIT09dW5kZWZpbmVkKXtCKz1cIiAgICBraWQ9XCIrbC5raWQrXCJcXG5cIn19ZWxzZXtpZihBPT09XCJleHRLZXlVc2FnZVwiKXt2YXIgdz10aGlzLmdldEV4dEV4dEtleVVzYWdlTmFtZSgpO0IrPVwiICAgIFwiK3cuam9pbihcIiwgXCIpK1wiXFxuXCJ9ZWxzZXtpZihBPT09XCJzdWJqZWN0QWx0TmFtZVwiKXt2YXIgdD10aGlzLmdldEV4dFN1YmplY3RBbHROYW1lMigpO0IrPVwiICAgIFwiK3QrXCJcXG5cIn1lbHNle2lmKEE9PT1cImNSTERpc3RyaWJ1dGlvblBvaW50c1wiKXt2YXIgeT10aGlzLmdldEV4dENSTERpc3RyaWJ1dGlvblBvaW50c1VSSSgpO0IrPVwiICAgIFwiK3krXCJcXG5cIn1lbHNle2lmKEE9PT1cImF1dGhvcml0eUluZm9BY2Nlc3NcIil7dmFyIHA9dGhpcy5nZXRFeHRBSUFJbmZvKCk7aWYocC5vY3NwIT09dW5kZWZpbmVkKXtCKz1cIiAgICBvY3NwOiBcIitwLm9jc3Auam9pbihcIixcIikrXCJcXG5cIn1pZihwLmNhaXNzdWVyIT09dW5kZWZpbmVkKXtCKz1cIiAgICBjYWlzc3VlcjogXCIrcC5jYWlzc3Vlci5qb2luKFwiLFwiKStcIlxcblwifX1lbHNle2lmKEE9PT1cImNlcnRpZmljYXRlUG9saWNpZXNcIil7dmFyIG89dGhpcy5nZXRFeHRDZXJ0aWZpY2F0ZVBvbGljaWVzKCk7Zm9yKHZhciBxPTA7cTxvLmxlbmd0aDtxKyspe2lmKG9bcV0uaWQhPT11bmRlZmluZWQpe0IrPVwiICAgIHBvbGljeSBvaWQ6IFwiK29bcV0uaWQrXCJcXG5cIn1pZihvW3FdLmNwcyE9PXVuZGVmaW5lZCl7Qis9XCIgICAgY3BzOiBcIitvW3FdLmNwcytcIlxcblwifX19fX19fX19fX19fUIrPVwic2lnbmF0dXJlIGFsZ29yaXRobTogXCIrdGhpcy5nZXRTaWduYXR1cmVBbGdvcml0aG1OYW1lKCkrXCJcXG5cIjtCKz1cInNpZ25hdHVyZTogXCIrdGhpcy5nZXRTaWduYXR1cmVWYWx1ZUhleCgpLnN1YnN0cigwLDE2KStcIi4uLlxcblwiO3JldHVybiBCfX1YNTA5LmhleDJkbj1mdW5jdGlvbihmLGIpe2lmKGI9PT11bmRlZmluZWQpe2I9MH1pZihmLnN1YnN0cihiLDIpIT09XCIzMFwiKXt0aHJvd1wibWFsZm9ybWVkIEROXCJ9dmFyIGM9bmV3IEFycmF5KCk7dmFyIGQ9QVNOMUhFWC5nZXRDaGlsZElkeChmLGIpO2Zvcih2YXIgZT0wO2U8ZC5sZW5ndGg7ZSsrKXtjLnB1c2goWDUwOS5oZXgycmRuKGYsZFtlXSkpfWM9Yy5tYXAoZnVuY3Rpb24oYSl7cmV0dXJuIGEucmVwbGFjZShcIi9cIixcIlxcXFwvXCIpfSk7cmV0dXJuXCIvXCIrYy5qb2luKFwiL1wiKX07WDUwOS5oZXgycmRuPWZ1bmN0aW9uKGYsYil7aWYoYj09PXVuZGVmaW5lZCl7Yj0wfWlmKGYuc3Vic3RyKGIsMikhPT1cIjMxXCIpe3Rocm93XCJtYWxmb3JtZWQgUkROXCJ9dmFyIGM9bmV3IEFycmF5KCk7dmFyIGQ9QVNOMUhFWC5nZXRDaGlsZElkeChmLGIpO2Zvcih2YXIgZT0wO2U8ZC5sZW5ndGg7ZSsrKXtjLnB1c2goWDUwOS5oZXgyYXR0clR5cGVWYWx1ZShmLGRbZV0pKX1jPWMubWFwKGZ1bmN0aW9uKGEpe3JldHVybiBhLnJlcGxhY2UoXCIrXCIsXCJcXFxcK1wiKX0pO3JldHVybiBjLmpvaW4oXCIrXCIpfTtYNTA5LmhleDJhdHRyVHlwZVZhbHVlPWZ1bmN0aW9uKGQsaSl7dmFyIGo9QVNOMUhFWDt2YXIgaD1qLmdldFY7aWYoaT09PXVuZGVmaW5lZCl7aT0wfWlmKGQuc3Vic3RyKGksMikhPT1cIjMwXCIpe3Rocm93XCJtYWxmb3JtZWQgYXR0cmlidXRlIHR5cGUgYW5kIHZhbHVlXCJ9dmFyIGc9ai5nZXRDaGlsZElkeChkLGkpO2lmKGcubGVuZ3RoIT09Mnx8ZC5zdWJzdHIoZ1swXSwyKSE9PVwiMDZcIil7XCJtYWxmb3JtZWQgYXR0cmlidXRlIHR5cGUgYW5kIHZhbHVlXCJ9dmFyIGI9aChkLGdbMF0pO3ZhciBmPUtKVVIuYXNuMS5BU04xVXRpbC5vaWRIZXhUb0ludChiKTt2YXIgZT1LSlVSLmFzbjEueDUwOS5PSUQub2lkMmF0eXBlKGYpO3ZhciBhPWgoZCxnWzFdKTt2YXIgYz1oZXh0b3JzdHIoYSk7cmV0dXJuIGUrXCI9XCIrY307WDUwOS5nZXRQdWJsaWNLZXlGcm9tQ2VydEhleD1mdW5jdGlvbihiKXt2YXIgYT1uZXcgWDUwOSgpO2EucmVhZENlcnRIZXgoYik7cmV0dXJuIGEuZ2V0UHVibGljS2V5KCl9O1g1MDkuZ2V0UHVibGljS2V5RnJvbUNlcnRQRU09ZnVuY3Rpb24oYil7dmFyIGE9bmV3IFg1MDkoKTthLnJlYWRDZXJ0UEVNKGIpO3JldHVybiBhLmdldFB1YmxpY0tleSgpfTtYNTA5LmdldFB1YmxpY0tleUluZm9Qcm9wT2ZDZXJ0UEVNPWZ1bmN0aW9uKGMpe3ZhciBlPUFTTjFIRVg7dmFyIGc9ZS5nZXRWYnlMaXN0O3ZhciBiPXt9O3ZhciBhLGYsZDtiLmFsZ3BhcmFtPW51bGw7YT1uZXcgWDUwOSgpO2EucmVhZENlcnRQRU0oYyk7Zj1hLmdldFB1YmxpY0tleUhleCgpO2Iua2V5aGV4PWcoZiwwLFsxXSxcIjAzXCIpLnN1YnN0cigyKTtiLmFsZ29pZD1nKGYsMCxbMCwwXSxcIjA2XCIpO2lmKGIuYWxnb2lkPT09XCIyYTg2NDhjZTNkMDIwMVwiKXtiLmFsZ3BhcmFtPWcoZiwwLFswLDFdLFwiMDZcIil9cmV0dXJuIGJ9O1g1MDkuS0VZVVNBR0VfTkFNRT1bXCJkaWdpdGFsU2lnbmF0dXJlXCIsXCJub25SZXB1ZGlhdGlvblwiLFwia2V5RW5jaXBoZXJtZW50XCIsXCJkYXRhRW5jaXBoZXJtZW50XCIsXCJrZXlBZ3JlZW1lbnRcIixcImtleUNlcnRTaWduXCIsXCJjUkxTaWduXCIsXCJlbmNpcGhlck9ubHlcIixcImRlY2lwaGVyT25seVwiXTtcbmlmKHR5cGVvZiBLSlVSPT1cInVuZGVmaW5lZFwifHwhS0pVUil7S0pVUj17fX1pZih0eXBlb2YgS0pVUi5qd3M9PVwidW5kZWZpbmVkXCJ8fCFLSlVSLmp3cyl7S0pVUi5qd3M9e319S0pVUi5qd3MuSldTPWZ1bmN0aW9uKCl7dmFyIGI9S0pVUixhPWIuandzLkpXUyxjPWEuaXNTYWZlSlNPTlN0cmluZzt0aGlzLnBhcnNlSldTPWZ1bmN0aW9uKGcsail7aWYoKHRoaXMucGFyc2VkSldTIT09dW5kZWZpbmVkKSYmKGp8fCh0aGlzLnBhcnNlZEpXUy5zaWd2YWxIIT09dW5kZWZpbmVkKSkpe3JldHVybn12YXIgaT1nLm1hdGNoKC9eKFteLl0rKVxcLihbXi5dKylcXC4oW14uXSspJC8pO2lmKGk9PW51bGwpe3Rocm93XCJKV1Mgc2lnbmF0dXJlIGlzIG5vdCBhIGZvcm0gb2YgJ0hlYWQuUGF5bG9hZC5TaWdWYWx1ZScuXCJ9dmFyIGs9aVsxXTt2YXIgZT1pWzJdO3ZhciBsPWlbM107dmFyIG49aytcIi5cIitlO3RoaXMucGFyc2VkSldTPXt9O3RoaXMucGFyc2VkSldTLmhlYWRCNjRVPWs7dGhpcy5wYXJzZWRKV1MucGF5bG9hZEI2NFU9ZTt0aGlzLnBhcnNlZEpXUy5zaWd2YWxCNjRVPWw7dGhpcy5wYXJzZWRKV1Muc2k9bjtpZighail7dmFyIGg9YjY0dXRvaGV4KGwpO3ZhciBmPXBhcnNlQmlnSW50KGgsMTYpO3RoaXMucGFyc2VkSldTLnNpZ3ZhbEg9aDt0aGlzLnBhcnNlZEpXUy5zaWd2YWxCST1mfXZhciBkPWI2NHV0b3V0Zjgoayk7dmFyIG09YjY0dXRvdXRmOChlKTt0aGlzLnBhcnNlZEpXUy5oZWFkUz1kO3RoaXMucGFyc2VkSldTLnBheWxvYWRTPW07aWYoIWMoZCx0aGlzLnBhcnNlZEpXUyxcImhlYWRQXCIpKXt0aHJvd1wibWFsZm9ybWVkIEpTT04gc3RyaW5nIGZvciBKV1MgSGVhZDogXCIrZH19fTtLSlVSLmp3cy5KV1Muc2lnbj1mdW5jdGlvbihpLHYseSx6LGEpe3ZhciB3PUtKVVIsbT13Lmp3cyxxPW0uSldTLGc9cS5yZWFkU2FmZUpTT05TdHJpbmcscD1xLmlzU2FmZUpTT05TdHJpbmcsZD13LmNyeXB0byxrPWQuRUNEU0Esbz1kLk1hYyxjPWQuU2lnbmF0dXJlLHQ9SlNPTjt2YXIgcyxqLG47aWYodHlwZW9mIHYhPVwic3RyaW5nXCImJnR5cGVvZiB2IT1cIm9iamVjdFwiKXt0aHJvd1wic3BIZWFkZXIgbXVzdCBiZSBKU09OIHN0cmluZyBvciBvYmplY3Q6IFwiK3Z9aWYodHlwZW9mIHY9PVwib2JqZWN0XCIpe2o9djtzPXQuc3RyaW5naWZ5KGopfWlmKHR5cGVvZiB2PT1cInN0cmluZ1wiKXtzPXY7aWYoIXAocykpe3Rocm93XCJKV1MgSGVhZCBpcyBub3Qgc2FmZSBKU09OIHN0cmluZzogXCIrc31qPWcocyl9bj15O2lmKHR5cGVvZiB5PT1cIm9iamVjdFwiKXtuPXQuc3RyaW5naWZ5KHkpfWlmKChpPT1cIlwifHxpPT1udWxsKSYmai5hbGchPT11bmRlZmluZWQpe2k9ai5hbGd9aWYoKGkhPVwiXCImJmkhPW51bGwpJiZqLmFsZz09PXVuZGVmaW5lZCl7ai5hbGc9aTtzPXQuc3RyaW5naWZ5KGopfWlmKGkhPT1qLmFsZyl7dGhyb3dcImFsZyBhbmQgc0hlYWRlci5hbGcgZG9lc24ndCBtYXRjaDogXCIraStcIiE9XCIrai5hbGd9dmFyIHI9bnVsbDtpZihxLmp3c2FsZzJzaWdhbGdbaV09PT11bmRlZmluZWQpe3Rocm93XCJ1bnN1cHBvcnRlZCBhbGcgbmFtZTogXCIraX1lbHNle3I9cS5qd3NhbGcyc2lnYWxnW2ldfXZhciBlPXV0Zjh0b2I2NHUocyk7dmFyIGw9dXRmOHRvYjY0dShuKTt2YXIgYj1lK1wiLlwiK2w7dmFyIHg9XCJcIjtpZihyLnN1YnN0cigwLDQpPT1cIkhtYWNcIil7aWYoej09PXVuZGVmaW5lZCl7dGhyb3dcIm1hYyBrZXkgc2hhbGwgYmUgc3BlY2lmaWVkIGZvciBIUyogYWxnXCJ9dmFyIGg9bmV3IG8oe2FsZzpyLHByb3Y6XCJjcnlwdG9qc1wiLHBhc3M6en0pO2gudXBkYXRlU3RyaW5nKGIpO3g9aC5kb0ZpbmFsKCl9ZWxzZXtpZihyLmluZGV4T2YoXCJ3aXRoRUNEU0FcIikhPS0xKXt2YXIgZj1uZXcgYyh7YWxnOnJ9KTtmLmluaXQoeixhKTtmLnVwZGF0ZVN0cmluZyhiKTtoQVNOMVNpZz1mLnNpZ24oKTt4PUtKVVIuY3J5cHRvLkVDRFNBLmFzbjFTaWdUb0NvbmNhdFNpZyhoQVNOMVNpZyl9ZWxzZXtpZihyIT1cIm5vbmVcIil7dmFyIGY9bmV3IGMoe2FsZzpyfSk7Zi5pbml0KHosYSk7Zi51cGRhdGVTdHJpbmcoYik7eD1mLnNpZ24oKX19fXZhciB1PWhleHRvYjY0dSh4KTtyZXR1cm4gYitcIi5cIit1fTtLSlVSLmp3cy5KV1MudmVyaWZ5PWZ1bmN0aW9uKHcsQixuKXt2YXIgeD1LSlVSLHE9eC5qd3MsdD1xLkpXUyxpPXQucmVhZFNhZmVKU09OU3RyaW5nLGU9eC5jcnlwdG8scD1lLkVDRFNBLHM9ZS5NYWMsZD1lLlNpZ25hdHVyZSxtO2lmKHR5cGVvZiBSU0FLZXkhPT11bmRlZmluZWQpe209UlNBS2V5fXZhciB5PXcuc3BsaXQoXCIuXCIpO2lmKHkubGVuZ3RoIT09Myl7cmV0dXJuIGZhbHNlfXZhciBmPXlbMF07dmFyIHI9eVsxXTt2YXIgYz1mK1wiLlwiK3I7dmFyIEE9YjY0dXRvaGV4KHlbMl0pO3ZhciBsPWkoYjY0dXRvdXRmOCh5WzBdKSk7dmFyIGs9bnVsbDt2YXIgej1udWxsO2lmKGwuYWxnPT09dW5kZWZpbmVkKXt0aHJvd1wiYWxnb3JpdGhtIG5vdCBzcGVjaWZpZWQgaW4gaGVhZGVyXCJ9ZWxzZXtrPWwuYWxnO3o9ay5zdWJzdHIoMCwyKX1pZihuIT1udWxsJiZPYmplY3QucHJvdG90eXBlLnRvU3RyaW5nLmNhbGwobik9PT1cIltvYmplY3QgQXJyYXldXCImJm4ubGVuZ3RoPjApe3ZhciBiPVwiOlwiK24uam9pbihcIjpcIikrXCI6XCI7aWYoYi5pbmRleE9mKFwiOlwiK2srXCI6XCIpPT0tMSl7dGhyb3dcImFsZ29yaXRobSAnXCIraytcIicgbm90IGFjY2VwdGVkIGluIHRoZSBsaXN0XCJ9fWlmKGshPVwibm9uZVwiJiZCPT09bnVsbCl7dGhyb3dcImtleSBzaGFsbCBiZSBzcGVjaWZpZWQgdG8gdmVyaWZ5LlwifWlmKHR5cGVvZiBCPT1cInN0cmluZ1wiJiZCLmluZGV4T2YoXCItLS0tLUJFR0lOIFwiKSE9LTEpe0I9S0VZVVRJTC5nZXRLZXkoQil9aWYoej09XCJSU1wifHx6PT1cIlBTXCIpe2lmKCEoQiBpbnN0YW5jZW9mIG0pKXt0aHJvd1wia2V5IHNoYWxsIGJlIGEgUlNBS2V5IG9iaiBmb3IgUlMqIGFuZCBQUyogYWxnc1wifX1pZih6PT1cIkVTXCIpe2lmKCEoQiBpbnN0YW5jZW9mIHApKXt0aHJvd1wia2V5IHNoYWxsIGJlIGEgRUNEU0Egb2JqIGZvciBFUyogYWxnc1wifX1pZihrPT1cIm5vbmVcIil7fXZhciB1PW51bGw7aWYodC5qd3NhbGcyc2lnYWxnW2wuYWxnXT09PXVuZGVmaW5lZCl7dGhyb3dcInVuc3VwcG9ydGVkIGFsZyBuYW1lOiBcIitrfWVsc2V7dT10Lmp3c2FsZzJzaWdhbGdba119aWYodT09XCJub25lXCIpe3Rocm93XCJub3Qgc3VwcG9ydGVkXCJ9ZWxzZXtpZih1LnN1YnN0cigwLDQpPT1cIkhtYWNcIil7dmFyIG89bnVsbDtpZihCPT09dW5kZWZpbmVkKXt0aHJvd1wiaGV4YWRlY2ltYWwga2V5IHNoYWxsIGJlIHNwZWNpZmllZCBmb3IgSE1BQ1wifXZhciBqPW5ldyBzKHthbGc6dSxwYXNzOkJ9KTtqLnVwZGF0ZVN0cmluZyhjKTtvPWouZG9GaW5hbCgpO3JldHVybiBBPT1vfWVsc2V7aWYodS5pbmRleE9mKFwid2l0aEVDRFNBXCIpIT0tMSl7dmFyIGg9bnVsbDt0cnl7aD1wLmNvbmNhdFNpZ1RvQVNOMVNpZyhBKX1jYXRjaCh2KXtyZXR1cm4gZmFsc2V9dmFyIGc9bmV3IGQoe2FsZzp1fSk7Zy5pbml0KEIpO2cudXBkYXRlU3RyaW5nKGMpO3JldHVybiBnLnZlcmlmeShoKX1lbHNle3ZhciBnPW5ldyBkKHthbGc6dX0pO2cuaW5pdChCKTtnLnVwZGF0ZVN0cmluZyhjKTtyZXR1cm4gZy52ZXJpZnkoQSl9fX19O0tKVVIuandzLkpXUy5wYXJzZT1mdW5jdGlvbihnKXt2YXIgYz1nLnNwbGl0KFwiLlwiKTt2YXIgYj17fTt2YXIgZixlLGQ7aWYoYy5sZW5ndGghPTImJmMubGVuZ3RoIT0zKXt0aHJvd1wibWFsZm9ybWVkIHNKV1M6IHdyb25nIG51bWJlciBvZiAnLicgc3BsaXR0ZWQgZWxlbWVudHNcIn1mPWNbMF07ZT1jWzFdO2lmKGMubGVuZ3RoPT0zKXtkPWNbMl19Yi5oZWFkZXJPYmo9S0pVUi5qd3MuSldTLnJlYWRTYWZlSlNPTlN0cmluZyhiNjR1dG91dGY4KGYpKTtiLnBheWxvYWRPYmo9S0pVUi5qd3MuSldTLnJlYWRTYWZlSlNPTlN0cmluZyhiNjR1dG91dGY4KGUpKTtiLmhlYWRlclBQPUpTT04uc3RyaW5naWZ5KGIuaGVhZGVyT2JqLG51bGwsXCIgIFwiKTtpZihiLnBheWxvYWRPYmo9PW51bGwpe2IucGF5bG9hZFBQPWI2NHV0b3V0ZjgoZSl9ZWxzZXtiLnBheWxvYWRQUD1KU09OLnN0cmluZ2lmeShiLnBheWxvYWRPYmosbnVsbCxcIiAgXCIpfWlmKGQhPT11bmRlZmluZWQpe2Iuc2lnSGV4PWI2NHV0b2hleChkKX1yZXR1cm4gYn07S0pVUi5qd3MuSldTLnZlcmlmeUpXVD1mdW5jdGlvbihlLGwscil7dmFyIGQ9S0pVUixqPWQuandzLG89ai5KV1Msbj1vLnJlYWRTYWZlSlNPTlN0cmluZyxwPW8uaW5BcnJheSxmPW8uaW5jbHVkZWRBcnJheTt2YXIgaz1lLnNwbGl0KFwiLlwiKTt2YXIgYz1rWzBdO3ZhciBpPWtbMV07dmFyIHE9YytcIi5cIitpO3ZhciBtPWI2NHV0b2hleChrWzJdKTt2YXIgaD1uKGI2NHV0b3V0ZjgoYykpO3ZhciBnPW4oYjY0dXRvdXRmOChpKSk7aWYoaC5hbGc9PT11bmRlZmluZWQpe3JldHVybiBmYWxzZX1pZihyLmFsZz09PXVuZGVmaW5lZCl7dGhyb3dcImFjY2VwdEZpZWxkLmFsZyBzaGFsbCBiZSBzcGVjaWZpZWRcIn1pZighcChoLmFsZyxyLmFsZykpe3JldHVybiBmYWxzZX1pZihnLmlzcyE9PXVuZGVmaW5lZCYmdHlwZW9mIHIuaXNzPT09XCJvYmplY3RcIil7aWYoIXAoZy5pc3Msci5pc3MpKXtyZXR1cm4gZmFsc2V9fWlmKGcuc3ViIT09dW5kZWZpbmVkJiZ0eXBlb2Ygci5zdWI9PT1cIm9iamVjdFwiKXtpZighcChnLnN1YixyLnN1Yikpe3JldHVybiBmYWxzZX19aWYoZy5hdWQhPT11bmRlZmluZWQmJnR5cGVvZiByLmF1ZD09PVwib2JqZWN0XCIpe2lmKHR5cGVvZiBnLmF1ZD09XCJzdHJpbmdcIil7aWYoIXAoZy5hdWQsci5hdWQpKXtyZXR1cm4gZmFsc2V9fWVsc2V7aWYodHlwZW9mIGcuYXVkPT1cIm9iamVjdFwiKXtpZighZihnLmF1ZCxyLmF1ZCkpe3JldHVybiBmYWxzZX19fX12YXIgYj1qLkludERhdGUuZ2V0Tm93KCk7aWYoci52ZXJpZnlBdCE9PXVuZGVmaW5lZCYmdHlwZW9mIHIudmVyaWZ5QXQ9PT1cIm51bWJlclwiKXtiPXIudmVyaWZ5QXR9aWYoci5ncmFjZVBlcmlvZD09PXVuZGVmaW5lZHx8dHlwZW9mIHIuZ3JhY2VQZXJpb2QhPT1cIm51bWJlclwiKXtyLmdyYWNlUGVyaW9kPTB9aWYoZy5leHAhPT11bmRlZmluZWQmJnR5cGVvZiBnLmV4cD09XCJudW1iZXJcIil7aWYoZy5leHArci5ncmFjZVBlcmlvZDxiKXtyZXR1cm4gZmFsc2V9fWlmKGcubmJmIT09dW5kZWZpbmVkJiZ0eXBlb2YgZy5uYmY9PVwibnVtYmVyXCIpe2lmKGI8Zy5uYmYtci5ncmFjZVBlcmlvZCl7cmV0dXJuIGZhbHNlfX1pZihnLmlhdCE9PXVuZGVmaW5lZCYmdHlwZW9mIGcuaWF0PT1cIm51bWJlclwiKXtpZihiPGcuaWF0LXIuZ3JhY2VQZXJpb2Qpe3JldHVybiBmYWxzZX19aWYoZy5qdGkhPT11bmRlZmluZWQmJnIuanRpIT09dW5kZWZpbmVkKXtpZihnLmp0aSE9PXIuanRpKXtyZXR1cm4gZmFsc2V9fWlmKCFvLnZlcmlmeShlLGwsci5hbGcpKXtyZXR1cm4gZmFsc2V9cmV0dXJuIHRydWV9O0tKVVIuandzLkpXUy5pbmNsdWRlZEFycmF5PWZ1bmN0aW9uKGIsYSl7dmFyIGM9S0pVUi5qd3MuSldTLmluQXJyYXk7aWYoYj09PW51bGwpe3JldHVybiBmYWxzZX1pZih0eXBlb2YgYiE9PVwib2JqZWN0XCIpe3JldHVybiBmYWxzZX1pZih0eXBlb2YgYi5sZW5ndGghPT1cIm51bWJlclwiKXtyZXR1cm4gZmFsc2V9Zm9yKHZhciBkPTA7ZDxiLmxlbmd0aDtkKyspe2lmKCFjKGJbZF0sYSkpe3JldHVybiBmYWxzZX19cmV0dXJuIHRydWV9O0tKVVIuandzLkpXUy5pbkFycmF5PWZ1bmN0aW9uKGQsYil7aWYoYj09PW51bGwpe3JldHVybiBmYWxzZX1pZih0eXBlb2YgYiE9PVwib2JqZWN0XCIpe3JldHVybiBmYWxzZX1pZih0eXBlb2YgYi5sZW5ndGghPT1cIm51bWJlclwiKXtyZXR1cm4gZmFsc2V9Zm9yKHZhciBjPTA7YzxiLmxlbmd0aDtjKyspe2lmKGJbY109PWQpe3JldHVybiB0cnVlfX1yZXR1cm4gZmFsc2V9O0tKVVIuandzLkpXUy5qd3NhbGcyc2lnYWxnPXtIUzI1NjpcIkhtYWNTSEEyNTZcIixIUzM4NDpcIkhtYWNTSEEzODRcIixIUzUxMjpcIkhtYWNTSEE1MTJcIixSUzI1NjpcIlNIQTI1NndpdGhSU0FcIixSUzM4NDpcIlNIQTM4NHdpdGhSU0FcIixSUzUxMjpcIlNIQTUxMndpdGhSU0FcIixFUzI1NjpcIlNIQTI1NndpdGhFQ0RTQVwiLEVTMzg0OlwiU0hBMzg0d2l0aEVDRFNBXCIsUFMyNTY6XCJTSEEyNTZ3aXRoUlNBYW5kTUdGMVwiLFBTMzg0OlwiU0hBMzg0d2l0aFJTQWFuZE1HRjFcIixQUzUxMjpcIlNIQTUxMndpdGhSU0FhbmRNR0YxXCIsbm9uZTpcIm5vbmVcIix9O0tKVVIuandzLkpXUy5pc1NhZmVKU09OU3RyaW5nPWZ1bmN0aW9uKGMsYixkKXt2YXIgZT1udWxsO3RyeXtlPWpzb25QYXJzZShjKTtpZih0eXBlb2YgZSE9XCJvYmplY3RcIil7cmV0dXJuIDB9aWYoZS5jb25zdHJ1Y3Rvcj09PUFycmF5KXtyZXR1cm4gMH1pZihiKXtiW2RdPWV9cmV0dXJuIDF9Y2F0Y2goYSl7cmV0dXJuIDB9fTtLSlVSLmp3cy5KV1MucmVhZFNhZmVKU09OU3RyaW5nPWZ1bmN0aW9uKGIpe3ZhciBjPW51bGw7dHJ5e2M9anNvblBhcnNlKGIpO2lmKHR5cGVvZiBjIT1cIm9iamVjdFwiKXtyZXR1cm4gbnVsbH1pZihjLmNvbnN0cnVjdG9yPT09QXJyYXkpe3JldHVybiBudWxsfXJldHVybiBjfWNhdGNoKGEpe3JldHVybiBudWxsfX07S0pVUi5qd3MuSldTLmdldEVuY29kZWRTaWduYXR1cmVWYWx1ZUZyb21KV1M9ZnVuY3Rpb24oYil7dmFyIGE9Yi5tYXRjaCgvXlteLl0rXFwuW14uXStcXC4oW14uXSspJC8pO2lmKGE9PW51bGwpe3Rocm93XCJKV1Mgc2lnbmF0dXJlIGlzIG5vdCBhIGZvcm0gb2YgJ0hlYWQuUGF5bG9hZC5TaWdWYWx1ZScuXCJ9cmV0dXJuIGFbMV19O0tKVVIuandzLkpXUy5nZXRKV0t0aHVtYnByaW50PWZ1bmN0aW9uKGQpe2lmKGQua3R5IT09XCJSU0FcIiYmZC5rdHkhPT1cIkVDXCImJmQua3R5IT09XCJvY3RcIil7dGhyb3dcInVuc3VwcG9ydGVkIGFsZ29yaXRobSBmb3IgSldLIFRodW1wcmludFwifXZhciBhPVwie1wiO2lmKGQua3R5PT09XCJSU0FcIil7aWYodHlwZW9mIGQubiE9XCJzdHJpbmdcInx8dHlwZW9mIGQuZSE9XCJzdHJpbmdcIil7dGhyb3dcIndyb25nIG4gYW5kIGUgdmFsdWUgZm9yIFJTQSBrZXlcIn1hKz0nXCJlXCI6XCInK2QuZSsnXCIsJzthKz0nXCJrdHlcIjpcIicrZC5rdHkrJ1wiLCc7YSs9J1wiblwiOlwiJytkLm4rJ1wifSd9ZWxzZXtpZihkLmt0eT09PVwiRUNcIil7aWYodHlwZW9mIGQuY3J2IT1cInN0cmluZ1wifHx0eXBlb2YgZC54IT1cInN0cmluZ1wifHx0eXBlb2YgZC55IT1cInN0cmluZ1wiKXt0aHJvd1wid3JvbmcgY3J2LCB4IGFuZCB5IHZhbHVlIGZvciBFQyBrZXlcIn1hKz0nXCJjcnZcIjpcIicrZC5jcnYrJ1wiLCc7YSs9J1wia3R5XCI6XCInK2Qua3R5KydcIiwnO2ErPSdcInhcIjpcIicrZC54KydcIiwnO2ErPSdcInlcIjpcIicrZC55KydcIn0nfWVsc2V7aWYoZC5rdHk9PT1cIm9jdFwiKXtpZih0eXBlb2YgZC5rIT1cInN0cmluZ1wiKXt0aHJvd1wid3JvbmcgayB2YWx1ZSBmb3Igb2N0KHN5bW1ldHJpYykga2V5XCJ9YSs9J1wia3R5XCI6XCInK2Qua3R5KydcIiwnO2ErPSdcImtcIjpcIicrZC5rKydcIn0nfX19dmFyIGI9cnN0cnRvaGV4KGEpO3ZhciBjPUtKVVIuY3J5cHRvLlV0aWwuaGFzaEhleChiLFwic2hhMjU2XCIpO3ZhciBlPWhleHRvYjY0dShjKTtyZXR1cm4gZX07S0pVUi5qd3MuSW50RGF0ZT17fTtLSlVSLmp3cy5JbnREYXRlLmdldD1mdW5jdGlvbihjKXt2YXIgYj1LSlVSLmp3cy5JbnREYXRlLGQ9Yi5nZXROb3csYT1iLmdldFp1bHU7aWYoYz09XCJub3dcIil7cmV0dXJuIGQoKX1lbHNle2lmKGM9PVwibm93ICsgMWhvdXJcIil7cmV0dXJuIGQoKSs2MCo2MH1lbHNle2lmKGM9PVwibm93ICsgMWRheVwiKXtyZXR1cm4gZCgpKzYwKjYwKjI0fWVsc2V7aWYoYz09XCJub3cgKyAxbW9udGhcIil7cmV0dXJuIGQoKSs2MCo2MCoyNCozMH1lbHNle2lmKGM9PVwibm93ICsgMXllYXJcIil7cmV0dXJuIGQoKSs2MCo2MCoyNCozNjV9ZWxzZXtpZihjLm1hdGNoKC9aJC8pKXtyZXR1cm4gYShjKX1lbHNle2lmKGMubWF0Y2goL15bMC05XSskLykpe3JldHVybiBwYXJzZUludChjKX19fX19fX10aHJvd1widW5zdXBwb3J0ZWQgZm9ybWF0OiBcIitjfTtLSlVSLmp3cy5JbnREYXRlLmdldFp1bHU9ZnVuY3Rpb24oYSl7cmV0dXJuIHp1bHV0b3NlYyhhKX07S0pVUi5qd3MuSW50RGF0ZS5nZXROb3c9ZnVuY3Rpb24oKXt2YXIgYT1+fihuZXcgRGF0ZSgpLzEwMDApO3JldHVybiBhfTtLSlVSLmp3cy5JbnREYXRlLmludERhdGUyVVRDU3RyaW5nPWZ1bmN0aW9uKGEpe3ZhciBiPW5ldyBEYXRlKGEqMTAwMCk7cmV0dXJuIGIudG9VVENTdHJpbmcoKX07S0pVUi5qd3MuSW50RGF0ZS5pbnREYXRlMlp1bHU9ZnVuY3Rpb24oZSl7dmFyIGk9bmV3IERhdGUoZSoxMDAwKSxoPShcIjAwMDBcIitpLmdldFVUQ0Z1bGxZZWFyKCkpLnNsaWNlKC00KSxnPShcIjAwXCIrKGkuZ2V0VVRDTW9udGgoKSsxKSkuc2xpY2UoLTIpLGI9KFwiMDBcIitpLmdldFVUQ0RhdGUoKSkuc2xpY2UoLTIpLGE9KFwiMDBcIitpLmdldFVUQ0hvdXJzKCkpLnNsaWNlKC0yKSxjPShcIjAwXCIraS5nZXRVVENNaW51dGVzKCkpLnNsaWNlKC0yKSxmPShcIjAwXCIraS5nZXRVVENTZWNvbmRzKCkpLnNsaWNlKC0yKTtyZXR1cm4gaCtnK2IrYStjK2YrXCJaXCJ9O1xuZXhwb3J0IHsgU2VjdXJlUmFuZG9tIH07XHJcbmV4cG9ydCB7IHJuZ19zZWVkX3RpbWUgfTtcclxuXHJcbmV4cG9ydCB7IEJpZ0ludGVnZXIgfTtcclxuZXhwb3J0IHsgUlNBS2V5IH07XHJcbmV4cG9ydCBjb25zdCB7IEVEU0EgfSA9IEtKVVIuY3J5cHRvO1xyXG5leHBvcnQgY29uc3QgeyBEU0EgfSA9IEtKVVIuY3J5cHRvO1xyXG5leHBvcnQgY29uc3QgeyBTaWduYXR1cmUgfSA9IEtKVVIuY3J5cHRvO1xyXG5leHBvcnQgY29uc3QgeyBNZXNzYWdlRGlnZXN0IH0gPSAgS0pVUi5jcnlwdG87XHJcbmV4cG9ydCBjb25zdCB7IE1hYyB9ID0gS0pVUi5jcnlwdG87XHJcbmV4cG9ydCBjb25zdCB7IENpcGhlciB9ID0gIEtKVVIuY3J5cHRvO1xyXG5leHBvcnQgeyBLRVlVVElMIH07XHJcbmV4cG9ydCB7IEFTTjFIRVggfTtcclxuZXhwb3J0IHsgWDUwOSB9O1xyXG5leHBvcnQgeyBDcnlwdG9KUyB9O1xyXG5cclxuLy8gZXh0L2Jhc2U2NC5qc1xyXG5leHBvcnQgeyBiNjR0b2hleCB9O1xyXG5leHBvcnQgeyBiNjR0b0JBIH07XHJcblxyXG4vLyBiYXNlNjR4LmpzXHJcbmV4cG9ydCB7IHN0b0JBIH07XHJcbmV4cG9ydCB7IEJBdG9zIH07XHJcbmV4cG9ydCB7IEJBdG9oZXggfTtcclxuZXhwb3J0IHsgc3RvaGV4IH07XHJcbmV4cG9ydCB7IHN0b2I2NCB9O1xyXG5leHBvcnQgeyBzdG9iNjR1IH07XHJcbmV4cG9ydCB7IGI2NHV0b3MgfTtcclxuZXhwb3J0IHsgYjY0dG9iNjR1IH07XHJcbmV4cG9ydCB7IGI2NHV0b2I2NCB9O1xyXG5leHBvcnQgeyBoZXgyYjY0IH07XHJcbmV4cG9ydCB7IGhleHRvYjY0dSB9O1xyXG5leHBvcnQgeyBiNjR1dG9oZXggfTtcclxuZXhwb3J0IHsgdXRmOHRvYjY0dSB9O1xyXG5leHBvcnQgeyBiNjR1dG91dGY4IH07XHJcbmV4cG9ydCB7IHV0Zjh0b2I2NCB9O1xyXG5leHBvcnQgeyBiNjR0b3V0ZjggfTtcclxuZXhwb3J0IHsgdXRmOHRvaGV4IH07XHJcbmV4cG9ydCB7IGhleHRvdXRmOCB9O1xyXG5leHBvcnQgeyBoZXh0b3JzdHIgfTtcclxuZXhwb3J0IHsgcnN0cnRvaGV4IH07XHJcbmV4cG9ydCB7IGhleHRvYjY0IH07XHJcbmV4cG9ydCB7IGhleHRvYjY0bmwgfTtcclxuZXhwb3J0IHsgYjY0bmx0b2hleCB9O1xyXG5leHBvcnQgeyBoZXh0b3BlbSB9O1xyXG5leHBvcnQgeyBwZW10b2hleCB9O1xyXG5leHBvcnQgeyBoZXh0b0FycmF5QnVmZmVyIH07XHJcbmV4cG9ydCB7IEFycmF5QnVmZmVydG9oZXggfTtcclxuZXhwb3J0IHsgenVsdXRvbXNlYyB9O1xyXG5leHBvcnQgeyB6dWx1dG9zZWMgfTtcclxuZXhwb3J0IHsgenVsdXRvZGF0ZSB9O1xyXG5leHBvcnQgeyBkYXRldG96dWx1IH07XHJcbmV4cG9ydCB7IHVyaWNtcHRvaGV4IH07XHJcbmV4cG9ydCB7IGhleHRvdXJpY21wIH07XHJcbmV4cG9ydCB7IGlwdjZ0b2hleCB9O1xyXG5leHBvcnQgeyBoZXh0b2lwdjYgfTtcclxuZXhwb3J0IHsgaGV4dG9pcCB9O1xyXG5leHBvcnQgeyBpcHRvaGV4IH07XHJcbmV4cG9ydCB7IGVuY29kZVVSSUNvbXBvbmVudEFsbCB9O1xyXG5leHBvcnQgeyBuZXdsaW5lX3RvVW5peCB9O1xyXG5leHBvcnQgeyBuZXdsaW5lX3RvRG9zIH07XHJcbmV4cG9ydCB7IGhleHRvcG9zaGV4IH07XHJcbmV4cG9ydCB7IGludGFyeXN0cnRvaGV4IH07XHJcbmV4cG9ydCB7IHN0cmRpZmZpZHggfTtcclxuXHJcbi8vIG5hbWUgc3BhY2VzXHJcbmV4cG9ydCB7IEtKVVIgfTtcclxuY29uc3QgX2NyeXB0byA9ICBLSlVSLmNyeXB0bztcclxuZXhwb3J0IHsgX2NyeXB0byBhcyBjcnlwdG8gfTtcclxuZXhwb3J0IGNvbnN0IHsgYXNuMSB9ID0gS0pVUjtcclxuZXhwb3J0IGNvbnN0IHsgandzIH0gPSBLSlVSO1xyXG5leHBvcnQgY29uc3QgeyBsYW5nIH0gPSBLSlVSO1xyXG5cclxuXHJcbiIsIlwidXNlIHN0cmljdFwiO1xuXG5yZXF1aXJlKFwiY29yZS1qcy9zaGltXCIpO1xuXG5yZXF1aXJlKFwicmVnZW5lcmF0b3ItcnVudGltZS9ydW50aW1lXCIpO1xuXG5yZXF1aXJlKFwiY29yZS1qcy9mbi9yZWdleHAvZXNjYXBlXCIpO1xuXG5pZiAoZ2xvYmFsLl9iYWJlbFBvbHlmaWxsKSB7XG4gIHRocm93IG5ldyBFcnJvcihcIm9ubHkgb25lIGluc3RhbmNlIG9mIGJhYmVsLXBvbHlmaWxsIGlzIGFsbG93ZWRcIik7XG59XG5nbG9iYWwuX2JhYmVsUG9seWZpbGwgPSB0cnVlO1xuXG52YXIgREVGSU5FX1BST1BFUlRZID0gXCJkZWZpbmVQcm9wZXJ0eVwiO1xuZnVuY3Rpb24gZGVmaW5lKE8sIGtleSwgdmFsdWUpIHtcbiAgT1trZXldIHx8IE9iamVjdFtERUZJTkVfUFJPUEVSVFldKE8sIGtleSwge1xuICAgIHdyaXRhYmxlOiB0cnVlLFxuICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgICB2YWx1ZTogdmFsdWVcbiAgfSk7XG59XG5cbmRlZmluZShTdHJpbmcucHJvdG90eXBlLCBcInBhZExlZnRcIiwgXCJcIi5wYWRTdGFydCk7XG5kZWZpbmUoU3RyaW5nLnByb3RvdHlwZSwgXCJwYWRSaWdodFwiLCBcIlwiLnBhZEVuZCk7XG5cblwicG9wLHJldmVyc2Usc2hpZnQsa2V5cyx2YWx1ZXMsZW50cmllcyxpbmRleE9mLGV2ZXJ5LHNvbWUsZm9yRWFjaCxtYXAsZmlsdGVyLGZpbmQsZmluZEluZGV4LGluY2x1ZGVzLGpvaW4sc2xpY2UsY29uY2F0LHB1c2gsc3BsaWNlLHVuc2hpZnQsc29ydCxsYXN0SW5kZXhPZixyZWR1Y2UscmVkdWNlUmlnaHQsY29weVdpdGhpbixmaWxsXCIuc3BsaXQoXCIsXCIpLmZvckVhY2goZnVuY3Rpb24gKGtleSkge1xuICBbXVtrZXldICYmIGRlZmluZShBcnJheSwga2V5LCBGdW5jdGlvbi5jYWxsLmJpbmQoW11ba2V5XSkpO1xufSk7IiwiLyoqXG4gKiBDb3B5cmlnaHQgKGMpIDIwMTQsIEZhY2Vib29rLCBJbmMuXG4gKiBBbGwgcmlnaHRzIHJlc2VydmVkLlxuICpcbiAqIFRoaXMgc291cmNlIGNvZGUgaXMgbGljZW5zZWQgdW5kZXIgdGhlIEJTRC1zdHlsZSBsaWNlbnNlIGZvdW5kIGluIHRoZVxuICogaHR0cHM6Ly9yYXcuZ2l0aHViLmNvbS9mYWNlYm9vay9yZWdlbmVyYXRvci9tYXN0ZXIvTElDRU5TRSBmaWxlLiBBblxuICogYWRkaXRpb25hbCBncmFudCBvZiBwYXRlbnQgcmlnaHRzIGNhbiBiZSBmb3VuZCBpbiB0aGUgUEFURU5UUyBmaWxlIGluXG4gKiB0aGUgc2FtZSBkaXJlY3RvcnkuXG4gKi9cblxuIShmdW5jdGlvbihnbG9iYWwpIHtcbiAgXCJ1c2Ugc3RyaWN0XCI7XG5cbiAgdmFyIE9wID0gT2JqZWN0LnByb3RvdHlwZTtcbiAgdmFyIGhhc093biA9IE9wLmhhc093blByb3BlcnR5O1xuICB2YXIgdW5kZWZpbmVkOyAvLyBNb3JlIGNvbXByZXNzaWJsZSB0aGFuIHZvaWQgMC5cbiAgdmFyICRTeW1ib2wgPSB0eXBlb2YgU3ltYm9sID09PSBcImZ1bmN0aW9uXCIgPyBTeW1ib2wgOiB7fTtcbiAgdmFyIGl0ZXJhdG9yU3ltYm9sID0gJFN5bWJvbC5pdGVyYXRvciB8fCBcIkBAaXRlcmF0b3JcIjtcbiAgdmFyIGFzeW5jSXRlcmF0b3JTeW1ib2wgPSAkU3ltYm9sLmFzeW5jSXRlcmF0b3IgfHwgXCJAQGFzeW5jSXRlcmF0b3JcIjtcbiAgdmFyIHRvU3RyaW5nVGFnU3ltYm9sID0gJFN5bWJvbC50b1N0cmluZ1RhZyB8fCBcIkBAdG9TdHJpbmdUYWdcIjtcblxuICB2YXIgaW5Nb2R1bGUgPSB0eXBlb2YgbW9kdWxlID09PSBcIm9iamVjdFwiO1xuICB2YXIgcnVudGltZSA9IGdsb2JhbC5yZWdlbmVyYXRvclJ1bnRpbWU7XG4gIGlmIChydW50aW1lKSB7XG4gICAgaWYgKGluTW9kdWxlKSB7XG4gICAgICAvLyBJZiByZWdlbmVyYXRvclJ1bnRpbWUgaXMgZGVmaW5lZCBnbG9iYWxseSBhbmQgd2UncmUgaW4gYSBtb2R1bGUsXG4gICAgICAvLyBtYWtlIHRoZSBleHBvcnRzIG9iamVjdCBpZGVudGljYWwgdG8gcmVnZW5lcmF0b3JSdW50aW1lLlxuICAgICAgbW9kdWxlLmV4cG9ydHMgPSBydW50aW1lO1xuICAgIH1cbiAgICAvLyBEb24ndCBib3RoZXIgZXZhbHVhdGluZyB0aGUgcmVzdCBvZiB0aGlzIGZpbGUgaWYgdGhlIHJ1bnRpbWUgd2FzXG4gICAgLy8gYWxyZWFkeSBkZWZpbmVkIGdsb2JhbGx5LlxuICAgIHJldHVybjtcbiAgfVxuXG4gIC8vIERlZmluZSB0aGUgcnVudGltZSBnbG9iYWxseSAoYXMgZXhwZWN0ZWQgYnkgZ2VuZXJhdGVkIGNvZGUpIGFzIGVpdGhlclxuICAvLyBtb2R1bGUuZXhwb3J0cyAoaWYgd2UncmUgaW4gYSBtb2R1bGUpIG9yIGEgbmV3LCBlbXB0eSBvYmplY3QuXG4gIHJ1bnRpbWUgPSBnbG9iYWwucmVnZW5lcmF0b3JSdW50aW1lID0gaW5Nb2R1bGUgPyBtb2R1bGUuZXhwb3J0cyA6IHt9O1xuXG4gIGZ1bmN0aW9uIHdyYXAoaW5uZXJGbiwgb3V0ZXJGbiwgc2VsZiwgdHJ5TG9jc0xpc3QpIHtcbiAgICAvLyBJZiBvdXRlckZuIHByb3ZpZGVkIGFuZCBvdXRlckZuLnByb3RvdHlwZSBpcyBhIEdlbmVyYXRvciwgdGhlbiBvdXRlckZuLnByb3RvdHlwZSBpbnN0YW5jZW9mIEdlbmVyYXRvci5cbiAgICB2YXIgcHJvdG9HZW5lcmF0b3IgPSBvdXRlckZuICYmIG91dGVyRm4ucHJvdG90eXBlIGluc3RhbmNlb2YgR2VuZXJhdG9yID8gb3V0ZXJGbiA6IEdlbmVyYXRvcjtcbiAgICB2YXIgZ2VuZXJhdG9yID0gT2JqZWN0LmNyZWF0ZShwcm90b0dlbmVyYXRvci5wcm90b3R5cGUpO1xuICAgIHZhciBjb250ZXh0ID0gbmV3IENvbnRleHQodHJ5TG9jc0xpc3QgfHwgW10pO1xuXG4gICAgLy8gVGhlIC5faW52b2tlIG1ldGhvZCB1bmlmaWVzIHRoZSBpbXBsZW1lbnRhdGlvbnMgb2YgdGhlIC5uZXh0LFxuICAgIC8vIC50aHJvdywgYW5kIC5yZXR1cm4gbWV0aG9kcy5cbiAgICBnZW5lcmF0b3IuX2ludm9rZSA9IG1ha2VJbnZva2VNZXRob2QoaW5uZXJGbiwgc2VsZiwgY29udGV4dCk7XG5cbiAgICByZXR1cm4gZ2VuZXJhdG9yO1xuICB9XG4gIHJ1bnRpbWUud3JhcCA9IHdyYXA7XG5cbiAgLy8gVHJ5L2NhdGNoIGhlbHBlciB0byBtaW5pbWl6ZSBkZW9wdGltaXphdGlvbnMuIFJldHVybnMgYSBjb21wbGV0aW9uXG4gIC8vIHJlY29yZCBsaWtlIGNvbnRleHQudHJ5RW50cmllc1tpXS5jb21wbGV0aW9uLiBUaGlzIGludGVyZmFjZSBjb3VsZFxuICAvLyBoYXZlIGJlZW4gKGFuZCB3YXMgcHJldmlvdXNseSkgZGVzaWduZWQgdG8gdGFrZSBhIGNsb3N1cmUgdG8gYmVcbiAgLy8gaW52b2tlZCB3aXRob3V0IGFyZ3VtZW50cywgYnV0IGluIGFsbCB0aGUgY2FzZXMgd2UgY2FyZSBhYm91dCB3ZVxuICAvLyBhbHJlYWR5IGhhdmUgYW4gZXhpc3RpbmcgbWV0aG9kIHdlIHdhbnQgdG8gY2FsbCwgc28gdGhlcmUncyBubyBuZWVkXG4gIC8vIHRvIGNyZWF0ZSBhIG5ldyBmdW5jdGlvbiBvYmplY3QuIFdlIGNhbiBldmVuIGdldCBhd2F5IHdpdGggYXNzdW1pbmdcbiAgLy8gdGhlIG1ldGhvZCB0YWtlcyBleGFjdGx5IG9uZSBhcmd1bWVudCwgc2luY2UgdGhhdCBoYXBwZW5zIHRvIGJlIHRydWVcbiAgLy8gaW4gZXZlcnkgY2FzZSwgc28gd2UgZG9uJ3QgaGF2ZSB0byB0b3VjaCB0aGUgYXJndW1lbnRzIG9iamVjdC4gVGhlXG4gIC8vIG9ubHkgYWRkaXRpb25hbCBhbGxvY2F0aW9uIHJlcXVpcmVkIGlzIHRoZSBjb21wbGV0aW9uIHJlY29yZCwgd2hpY2hcbiAgLy8gaGFzIGEgc3RhYmxlIHNoYXBlIGFuZCBzbyBob3BlZnVsbHkgc2hvdWxkIGJlIGNoZWFwIHRvIGFsbG9jYXRlLlxuICBmdW5jdGlvbiB0cnlDYXRjaChmbiwgb2JqLCBhcmcpIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuIHsgdHlwZTogXCJub3JtYWxcIiwgYXJnOiBmbi5jYWxsKG9iaiwgYXJnKSB9O1xuICAgIH0gY2F0Y2ggKGVycikge1xuICAgICAgcmV0dXJuIHsgdHlwZTogXCJ0aHJvd1wiLCBhcmc6IGVyciB9O1xuICAgIH1cbiAgfVxuXG4gIHZhciBHZW5TdGF0ZVN1c3BlbmRlZFN0YXJ0ID0gXCJzdXNwZW5kZWRTdGFydFwiO1xuICB2YXIgR2VuU3RhdGVTdXNwZW5kZWRZaWVsZCA9IFwic3VzcGVuZGVkWWllbGRcIjtcbiAgdmFyIEdlblN0YXRlRXhlY3V0aW5nID0gXCJleGVjdXRpbmdcIjtcbiAgdmFyIEdlblN0YXRlQ29tcGxldGVkID0gXCJjb21wbGV0ZWRcIjtcblxuICAvLyBSZXR1cm5pbmcgdGhpcyBvYmplY3QgZnJvbSB0aGUgaW5uZXJGbiBoYXMgdGhlIHNhbWUgZWZmZWN0IGFzXG4gIC8vIGJyZWFraW5nIG91dCBvZiB0aGUgZGlzcGF0Y2ggc3dpdGNoIHN0YXRlbWVudC5cbiAgdmFyIENvbnRpbnVlU2VudGluZWwgPSB7fTtcblxuICAvLyBEdW1teSBjb25zdHJ1Y3RvciBmdW5jdGlvbnMgdGhhdCB3ZSB1c2UgYXMgdGhlIC5jb25zdHJ1Y3RvciBhbmRcbiAgLy8gLmNvbnN0cnVjdG9yLnByb3RvdHlwZSBwcm9wZXJ0aWVzIGZvciBmdW5jdGlvbnMgdGhhdCByZXR1cm4gR2VuZXJhdG9yXG4gIC8vIG9iamVjdHMuIEZvciBmdWxsIHNwZWMgY29tcGxpYW5jZSwgeW91IG1heSB3aXNoIHRvIGNvbmZpZ3VyZSB5b3VyXG4gIC8vIG1pbmlmaWVyIG5vdCB0byBtYW5nbGUgdGhlIG5hbWVzIG9mIHRoZXNlIHR3byBmdW5jdGlvbnMuXG4gIGZ1bmN0aW9uIEdlbmVyYXRvcigpIHt9XG4gIGZ1bmN0aW9uIEdlbmVyYXRvckZ1bmN0aW9uKCkge31cbiAgZnVuY3Rpb24gR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGUoKSB7fVxuXG4gIC8vIFRoaXMgaXMgYSBwb2x5ZmlsbCBmb3IgJUl0ZXJhdG9yUHJvdG90eXBlJSBmb3IgZW52aXJvbm1lbnRzIHRoYXRcbiAgLy8gZG9uJ3QgbmF0aXZlbHkgc3VwcG9ydCBpdC5cbiAgdmFyIEl0ZXJhdG9yUHJvdG90eXBlID0ge307XG4gIEl0ZXJhdG9yUHJvdG90eXBlW2l0ZXJhdG9yU3ltYm9sXSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcztcbiAgfTtcblxuICB2YXIgZ2V0UHJvdG8gPSBPYmplY3QuZ2V0UHJvdG90eXBlT2Y7XG4gIHZhciBOYXRpdmVJdGVyYXRvclByb3RvdHlwZSA9IGdldFByb3RvICYmIGdldFByb3RvKGdldFByb3RvKHZhbHVlcyhbXSkpKTtcbiAgaWYgKE5hdGl2ZUl0ZXJhdG9yUHJvdG90eXBlICYmXG4gICAgICBOYXRpdmVJdGVyYXRvclByb3RvdHlwZSAhPT0gT3AgJiZcbiAgICAgIGhhc093bi5jYWxsKE5hdGl2ZUl0ZXJhdG9yUHJvdG90eXBlLCBpdGVyYXRvclN5bWJvbCkpIHtcbiAgICAvLyBUaGlzIGVudmlyb25tZW50IGhhcyBhIG5hdGl2ZSAlSXRlcmF0b3JQcm90b3R5cGUlOyB1c2UgaXQgaW5zdGVhZFxuICAgIC8vIG9mIHRoZSBwb2x5ZmlsbC5cbiAgICBJdGVyYXRvclByb3RvdHlwZSA9IE5hdGl2ZUl0ZXJhdG9yUHJvdG90eXBlO1xuICB9XG5cbiAgdmFyIEdwID0gR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGUucHJvdG90eXBlID1cbiAgICBHZW5lcmF0b3IucHJvdG90eXBlID0gT2JqZWN0LmNyZWF0ZShJdGVyYXRvclByb3RvdHlwZSk7XG4gIEdlbmVyYXRvckZ1bmN0aW9uLnByb3RvdHlwZSA9IEdwLmNvbnN0cnVjdG9yID0gR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGU7XG4gIEdlbmVyYXRvckZ1bmN0aW9uUHJvdG90eXBlLmNvbnN0cnVjdG9yID0gR2VuZXJhdG9yRnVuY3Rpb247XG4gIEdlbmVyYXRvckZ1bmN0aW9uUHJvdG90eXBlW3RvU3RyaW5nVGFnU3ltYm9sXSA9XG4gICAgR2VuZXJhdG9yRnVuY3Rpb24uZGlzcGxheU5hbWUgPSBcIkdlbmVyYXRvckZ1bmN0aW9uXCI7XG5cbiAgLy8gSGVscGVyIGZvciBkZWZpbmluZyB0aGUgLm5leHQsIC50aHJvdywgYW5kIC5yZXR1cm4gbWV0aG9kcyBvZiB0aGVcbiAgLy8gSXRlcmF0b3IgaW50ZXJmYWNlIGluIHRlcm1zIG9mIGEgc2luZ2xlIC5faW52b2tlIG1ldGhvZC5cbiAgZnVuY3Rpb24gZGVmaW5lSXRlcmF0b3JNZXRob2RzKHByb3RvdHlwZSkge1xuICAgIFtcIm5leHRcIiwgXCJ0aHJvd1wiLCBcInJldHVyblwiXS5mb3JFYWNoKGZ1bmN0aW9uKG1ldGhvZCkge1xuICAgICAgcHJvdG90eXBlW21ldGhvZF0gPSBmdW5jdGlvbihhcmcpIHtcbiAgICAgICAgcmV0dXJuIHRoaXMuX2ludm9rZShtZXRob2QsIGFyZyk7XG4gICAgICB9O1xuICAgIH0pO1xuICB9XG5cbiAgcnVudGltZS5pc0dlbmVyYXRvckZ1bmN0aW9uID0gZnVuY3Rpb24oZ2VuRnVuKSB7XG4gICAgdmFyIGN0b3IgPSB0eXBlb2YgZ2VuRnVuID09PSBcImZ1bmN0aW9uXCIgJiYgZ2VuRnVuLmNvbnN0cnVjdG9yO1xuICAgIHJldHVybiBjdG9yXG4gICAgICA/IGN0b3IgPT09IEdlbmVyYXRvckZ1bmN0aW9uIHx8XG4gICAgICAgIC8vIEZvciB0aGUgbmF0aXZlIEdlbmVyYXRvckZ1bmN0aW9uIGNvbnN0cnVjdG9yLCB0aGUgYmVzdCB3ZSBjYW5cbiAgICAgICAgLy8gZG8gaXMgdG8gY2hlY2sgaXRzIC5uYW1lIHByb3BlcnR5LlxuICAgICAgICAoY3Rvci5kaXNwbGF5TmFtZSB8fCBjdG9yLm5hbWUpID09PSBcIkdlbmVyYXRvckZ1bmN0aW9uXCJcbiAgICAgIDogZmFsc2U7XG4gIH07XG5cbiAgcnVudGltZS5tYXJrID0gZnVuY3Rpb24oZ2VuRnVuKSB7XG4gICAgaWYgKE9iamVjdC5zZXRQcm90b3R5cGVPZikge1xuICAgICAgT2JqZWN0LnNldFByb3RvdHlwZU9mKGdlbkZ1biwgR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGUpO1xuICAgIH0gZWxzZSB7XG4gICAgICBnZW5GdW4uX19wcm90b19fID0gR2VuZXJhdG9yRnVuY3Rpb25Qcm90b3R5cGU7XG4gICAgICBpZiAoISh0b1N0cmluZ1RhZ1N5bWJvbCBpbiBnZW5GdW4pKSB7XG4gICAgICAgIGdlbkZ1blt0b1N0cmluZ1RhZ1N5bWJvbF0gPSBcIkdlbmVyYXRvckZ1bmN0aW9uXCI7XG4gICAgICB9XG4gICAgfVxuICAgIGdlbkZ1bi5wcm90b3R5cGUgPSBPYmplY3QuY3JlYXRlKEdwKTtcbiAgICByZXR1cm4gZ2VuRnVuO1xuICB9O1xuXG4gIC8vIFdpdGhpbiB0aGUgYm9keSBvZiBhbnkgYXN5bmMgZnVuY3Rpb24sIGBhd2FpdCB4YCBpcyB0cmFuc2Zvcm1lZCB0b1xuICAvLyBgeWllbGQgcmVnZW5lcmF0b3JSdW50aW1lLmF3cmFwKHgpYCwgc28gdGhhdCB0aGUgcnVudGltZSBjYW4gdGVzdFxuICAvLyBgaGFzT3duLmNhbGwodmFsdWUsIFwiX19hd2FpdFwiKWAgdG8gZGV0ZXJtaW5lIGlmIHRoZSB5aWVsZGVkIHZhbHVlIGlzXG4gIC8vIG1lYW50IHRvIGJlIGF3YWl0ZWQuXG4gIHJ1bnRpbWUuYXdyYXAgPSBmdW5jdGlvbihhcmcpIHtcbiAgICByZXR1cm4geyBfX2F3YWl0OiBhcmcgfTtcbiAgfTtcblxuICBmdW5jdGlvbiBBc3luY0l0ZXJhdG9yKGdlbmVyYXRvcikge1xuICAgIGZ1bmN0aW9uIGludm9rZShtZXRob2QsIGFyZywgcmVzb2x2ZSwgcmVqZWN0KSB7XG4gICAgICB2YXIgcmVjb3JkID0gdHJ5Q2F0Y2goZ2VuZXJhdG9yW21ldGhvZF0sIGdlbmVyYXRvciwgYXJnKTtcbiAgICAgIGlmIChyZWNvcmQudHlwZSA9PT0gXCJ0aHJvd1wiKSB7XG4gICAgICAgIHJlamVjdChyZWNvcmQuYXJnKTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIHZhciByZXN1bHQgPSByZWNvcmQuYXJnO1xuICAgICAgICB2YXIgdmFsdWUgPSByZXN1bHQudmFsdWU7XG4gICAgICAgIGlmICh2YWx1ZSAmJlxuICAgICAgICAgICAgdHlwZW9mIHZhbHVlID09PSBcIm9iamVjdFwiICYmXG4gICAgICAgICAgICBoYXNPd24uY2FsbCh2YWx1ZSwgXCJfX2F3YWl0XCIpKSB7XG4gICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh2YWx1ZS5fX2F3YWl0KS50aGVuKGZ1bmN0aW9uKHZhbHVlKSB7XG4gICAgICAgICAgICBpbnZva2UoXCJuZXh0XCIsIHZhbHVlLCByZXNvbHZlLCByZWplY3QpO1xuICAgICAgICAgIH0sIGZ1bmN0aW9uKGVycikge1xuICAgICAgICAgICAgaW52b2tlKFwidGhyb3dcIiwgZXJyLCByZXNvbHZlLCByZWplY3QpO1xuICAgICAgICAgIH0pO1xuICAgICAgICB9XG5cbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSh2YWx1ZSkudGhlbihmdW5jdGlvbih1bndyYXBwZWQpIHtcbiAgICAgICAgICAvLyBXaGVuIGEgeWllbGRlZCBQcm9taXNlIGlzIHJlc29sdmVkLCBpdHMgZmluYWwgdmFsdWUgYmVjb21lc1xuICAgICAgICAgIC8vIHRoZSAudmFsdWUgb2YgdGhlIFByb21pc2U8e3ZhbHVlLGRvbmV9PiByZXN1bHQgZm9yIHRoZVxuICAgICAgICAgIC8vIGN1cnJlbnQgaXRlcmF0aW9uLiBJZiB0aGUgUHJvbWlzZSBpcyByZWplY3RlZCwgaG93ZXZlciwgdGhlXG4gICAgICAgICAgLy8gcmVzdWx0IGZvciB0aGlzIGl0ZXJhdGlvbiB3aWxsIGJlIHJlamVjdGVkIHdpdGggdGhlIHNhbWVcbiAgICAgICAgICAvLyByZWFzb24uIE5vdGUgdGhhdCByZWplY3Rpb25zIG9mIHlpZWxkZWQgUHJvbWlzZXMgYXJlIG5vdFxuICAgICAgICAgIC8vIHRocm93biBiYWNrIGludG8gdGhlIGdlbmVyYXRvciBmdW5jdGlvbiwgYXMgaXMgdGhlIGNhc2VcbiAgICAgICAgICAvLyB3aGVuIGFuIGF3YWl0ZWQgUHJvbWlzZSBpcyByZWplY3RlZC4gVGhpcyBkaWZmZXJlbmNlIGluXG4gICAgICAgICAgLy8gYmVoYXZpb3IgYmV0d2VlbiB5aWVsZCBhbmQgYXdhaXQgaXMgaW1wb3J0YW50LCBiZWNhdXNlIGl0XG4gICAgICAgICAgLy8gYWxsb3dzIHRoZSBjb25zdW1lciB0byBkZWNpZGUgd2hhdCB0byBkbyB3aXRoIHRoZSB5aWVsZGVkXG4gICAgICAgICAgLy8gcmVqZWN0aW9uIChzd2FsbG93IGl0IGFuZCBjb250aW51ZSwgbWFudWFsbHkgLnRocm93IGl0IGJhY2tcbiAgICAgICAgICAvLyBpbnRvIHRoZSBnZW5lcmF0b3IsIGFiYW5kb24gaXRlcmF0aW9uLCB3aGF0ZXZlcikuIFdpdGhcbiAgICAgICAgICAvLyBhd2FpdCwgYnkgY29udHJhc3QsIHRoZXJlIGlzIG5vIG9wcG9ydHVuaXR5IHRvIGV4YW1pbmUgdGhlXG4gICAgICAgICAgLy8gcmVqZWN0aW9uIHJlYXNvbiBvdXRzaWRlIHRoZSBnZW5lcmF0b3IgZnVuY3Rpb24sIHNvIHRoZVxuICAgICAgICAgIC8vIG9ubHkgb3B0aW9uIGlzIHRvIHRocm93IGl0IGZyb20gdGhlIGF3YWl0IGV4cHJlc3Npb24sIGFuZFxuICAgICAgICAgIC8vIGxldCB0aGUgZ2VuZXJhdG9yIGZ1bmN0aW9uIGhhbmRsZSB0aGUgZXhjZXB0aW9uLlxuICAgICAgICAgIHJlc3VsdC52YWx1ZSA9IHVud3JhcHBlZDtcbiAgICAgICAgICByZXNvbHZlKHJlc3VsdCk7XG4gICAgICAgIH0sIHJlamVjdCk7XG4gICAgICB9XG4gICAgfVxuXG4gICAgaWYgKHR5cGVvZiBnbG9iYWwucHJvY2VzcyA9PT0gXCJvYmplY3RcIiAmJiBnbG9iYWwucHJvY2Vzcy5kb21haW4pIHtcbiAgICAgIGludm9rZSA9IGdsb2JhbC5wcm9jZXNzLmRvbWFpbi5iaW5kKGludm9rZSk7XG4gICAgfVxuXG4gICAgdmFyIHByZXZpb3VzUHJvbWlzZTtcblxuICAgIGZ1bmN0aW9uIGVucXVldWUobWV0aG9kLCBhcmcpIHtcbiAgICAgIGZ1bmN0aW9uIGNhbGxJbnZva2VXaXRoTWV0aG9kQW5kQXJnKCkge1xuICAgICAgICByZXR1cm4gbmV3IFByb21pc2UoZnVuY3Rpb24ocmVzb2x2ZSwgcmVqZWN0KSB7XG4gICAgICAgICAgaW52b2tlKG1ldGhvZCwgYXJnLCByZXNvbHZlLCByZWplY3QpO1xuICAgICAgICB9KTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHByZXZpb3VzUHJvbWlzZSA9XG4gICAgICAgIC8vIElmIGVucXVldWUgaGFzIGJlZW4gY2FsbGVkIGJlZm9yZSwgdGhlbiB3ZSB3YW50IHRvIHdhaXQgdW50aWxcbiAgICAgICAgLy8gYWxsIHByZXZpb3VzIFByb21pc2VzIGhhdmUgYmVlbiByZXNvbHZlZCBiZWZvcmUgY2FsbGluZyBpbnZva2UsXG4gICAgICAgIC8vIHNvIHRoYXQgcmVzdWx0cyBhcmUgYWx3YXlzIGRlbGl2ZXJlZCBpbiB0aGUgY29ycmVjdCBvcmRlci4gSWZcbiAgICAgICAgLy8gZW5xdWV1ZSBoYXMgbm90IGJlZW4gY2FsbGVkIGJlZm9yZSwgdGhlbiBpdCBpcyBpbXBvcnRhbnQgdG9cbiAgICAgICAgLy8gY2FsbCBpbnZva2UgaW1tZWRpYXRlbHksIHdpdGhvdXQgd2FpdGluZyBvbiBhIGNhbGxiYWNrIHRvIGZpcmUsXG4gICAgICAgIC8vIHNvIHRoYXQgdGhlIGFzeW5jIGdlbmVyYXRvciBmdW5jdGlvbiBoYXMgdGhlIG9wcG9ydHVuaXR5IHRvIGRvXG4gICAgICAgIC8vIGFueSBuZWNlc3Nhcnkgc2V0dXAgaW4gYSBwcmVkaWN0YWJsZSB3YXkuIFRoaXMgcHJlZGljdGFiaWxpdHlcbiAgICAgICAgLy8gaXMgd2h5IHRoZSBQcm9taXNlIGNvbnN0cnVjdG9yIHN5bmNocm9ub3VzbHkgaW52b2tlcyBpdHNcbiAgICAgICAgLy8gZXhlY3V0b3IgY2FsbGJhY2ssIGFuZCB3aHkgYXN5bmMgZnVuY3Rpb25zIHN5bmNocm9ub3VzbHlcbiAgICAgICAgLy8gZXhlY3V0ZSBjb2RlIGJlZm9yZSB0aGUgZmlyc3QgYXdhaXQuIFNpbmNlIHdlIGltcGxlbWVudCBzaW1wbGVcbiAgICAgICAgLy8gYXN5bmMgZnVuY3Rpb25zIGluIHRlcm1zIG9mIGFzeW5jIGdlbmVyYXRvcnMsIGl0IGlzIGVzcGVjaWFsbHlcbiAgICAgICAgLy8gaW1wb3J0YW50IHRvIGdldCB0aGlzIHJpZ2h0LCBldmVuIHRob3VnaCBpdCByZXF1aXJlcyBjYXJlLlxuICAgICAgICBwcmV2aW91c1Byb21pc2UgPyBwcmV2aW91c1Byb21pc2UudGhlbihcbiAgICAgICAgICBjYWxsSW52b2tlV2l0aE1ldGhvZEFuZEFyZyxcbiAgICAgICAgICAvLyBBdm9pZCBwcm9wYWdhdGluZyBmYWlsdXJlcyB0byBQcm9taXNlcyByZXR1cm5lZCBieSBsYXRlclxuICAgICAgICAgIC8vIGludm9jYXRpb25zIG9mIHRoZSBpdGVyYXRvci5cbiAgICAgICAgICBjYWxsSW52b2tlV2l0aE1ldGhvZEFuZEFyZ1xuICAgICAgICApIDogY2FsbEludm9rZVdpdGhNZXRob2RBbmRBcmcoKTtcbiAgICB9XG5cbiAgICAvLyBEZWZpbmUgdGhlIHVuaWZpZWQgaGVscGVyIG1ldGhvZCB0aGF0IGlzIHVzZWQgdG8gaW1wbGVtZW50IC5uZXh0LFxuICAgIC8vIC50aHJvdywgYW5kIC5yZXR1cm4gKHNlZSBkZWZpbmVJdGVyYXRvck1ldGhvZHMpLlxuICAgIHRoaXMuX2ludm9rZSA9IGVucXVldWU7XG4gIH1cblxuICBkZWZpbmVJdGVyYXRvck1ldGhvZHMoQXN5bmNJdGVyYXRvci5wcm90b3R5cGUpO1xuICBBc3luY0l0ZXJhdG9yLnByb3RvdHlwZVthc3luY0l0ZXJhdG9yU3ltYm9sXSA9IGZ1bmN0aW9uICgpIHtcbiAgICByZXR1cm4gdGhpcztcbiAgfTtcbiAgcnVudGltZS5Bc3luY0l0ZXJhdG9yID0gQXN5bmNJdGVyYXRvcjtcblxuICAvLyBOb3RlIHRoYXQgc2ltcGxlIGFzeW5jIGZ1bmN0aW9ucyBhcmUgaW1wbGVtZW50ZWQgb24gdG9wIG9mXG4gIC8vIEFzeW5jSXRlcmF0b3Igb2JqZWN0czsgdGhleSBqdXN0IHJldHVybiBhIFByb21pc2UgZm9yIHRoZSB2YWx1ZSBvZlxuICAvLyB0aGUgZmluYWwgcmVzdWx0IHByb2R1Y2VkIGJ5IHRoZSBpdGVyYXRvci5cbiAgcnVudGltZS5hc3luYyA9IGZ1bmN0aW9uKGlubmVyRm4sIG91dGVyRm4sIHNlbGYsIHRyeUxvY3NMaXN0KSB7XG4gICAgdmFyIGl0ZXIgPSBuZXcgQXN5bmNJdGVyYXRvcihcbiAgICAgIHdyYXAoaW5uZXJGbiwgb3V0ZXJGbiwgc2VsZiwgdHJ5TG9jc0xpc3QpXG4gICAgKTtcblxuICAgIHJldHVybiBydW50aW1lLmlzR2VuZXJhdG9yRnVuY3Rpb24ob3V0ZXJGbilcbiAgICAgID8gaXRlciAvLyBJZiBvdXRlckZuIGlzIGEgZ2VuZXJhdG9yLCByZXR1cm4gdGhlIGZ1bGwgaXRlcmF0b3IuXG4gICAgICA6IGl0ZXIubmV4dCgpLnRoZW4oZnVuY3Rpb24ocmVzdWx0KSB7XG4gICAgICAgICAgcmV0dXJuIHJlc3VsdC5kb25lID8gcmVzdWx0LnZhbHVlIDogaXRlci5uZXh0KCk7XG4gICAgICAgIH0pO1xuICB9O1xuXG4gIGZ1bmN0aW9uIG1ha2VJbnZva2VNZXRob2QoaW5uZXJGbiwgc2VsZiwgY29udGV4dCkge1xuICAgIHZhciBzdGF0ZSA9IEdlblN0YXRlU3VzcGVuZGVkU3RhcnQ7XG5cbiAgICByZXR1cm4gZnVuY3Rpb24gaW52b2tlKG1ldGhvZCwgYXJnKSB7XG4gICAgICBpZiAoc3RhdGUgPT09IEdlblN0YXRlRXhlY3V0aW5nKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcihcIkdlbmVyYXRvciBpcyBhbHJlYWR5IHJ1bm5pbmdcIik7XG4gICAgICB9XG5cbiAgICAgIGlmIChzdGF0ZSA9PT0gR2VuU3RhdGVDb21wbGV0ZWQpIHtcbiAgICAgICAgaWYgKG1ldGhvZCA9PT0gXCJ0aHJvd1wiKSB7XG4gICAgICAgICAgdGhyb3cgYXJnO1xuICAgICAgICB9XG5cbiAgICAgICAgLy8gQmUgZm9yZ2l2aW5nLCBwZXIgMjUuMy4zLjMuMyBvZiB0aGUgc3BlYzpcbiAgICAgICAgLy8gaHR0cHM6Ly9wZW9wbGUubW96aWxsYS5vcmcvfmpvcmVuZG9yZmYvZXM2LWRyYWZ0Lmh0bWwjc2VjLWdlbmVyYXRvcnJlc3VtZVxuICAgICAgICByZXR1cm4gZG9uZVJlc3VsdCgpO1xuICAgICAgfVxuXG4gICAgICBjb250ZXh0Lm1ldGhvZCA9IG1ldGhvZDtcbiAgICAgIGNvbnRleHQuYXJnID0gYXJnO1xuXG4gICAgICB3aGlsZSAodHJ1ZSkge1xuICAgICAgICB2YXIgZGVsZWdhdGUgPSBjb250ZXh0LmRlbGVnYXRlO1xuICAgICAgICBpZiAoZGVsZWdhdGUpIHtcbiAgICAgICAgICB2YXIgZGVsZWdhdGVSZXN1bHQgPSBtYXliZUludm9rZURlbGVnYXRlKGRlbGVnYXRlLCBjb250ZXh0KTtcbiAgICAgICAgICBpZiAoZGVsZWdhdGVSZXN1bHQpIHtcbiAgICAgICAgICAgIGlmIChkZWxlZ2F0ZVJlc3VsdCA9PT0gQ29udGludWVTZW50aW5lbCkgY29udGludWU7XG4gICAgICAgICAgICByZXR1cm4gZGVsZWdhdGVSZXN1bHQ7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGNvbnRleHQubWV0aG9kID09PSBcIm5leHRcIikge1xuICAgICAgICAgIC8vIFNldHRpbmcgY29udGV4dC5fc2VudCBmb3IgbGVnYWN5IHN1cHBvcnQgb2YgQmFiZWwnc1xuICAgICAgICAgIC8vIGZ1bmN0aW9uLnNlbnQgaW1wbGVtZW50YXRpb24uXG4gICAgICAgICAgY29udGV4dC5zZW50ID0gY29udGV4dC5fc2VudCA9IGNvbnRleHQuYXJnO1xuXG4gICAgICAgIH0gZWxzZSBpZiAoY29udGV4dC5tZXRob2QgPT09IFwidGhyb3dcIikge1xuICAgICAgICAgIGlmIChzdGF0ZSA9PT0gR2VuU3RhdGVTdXNwZW5kZWRTdGFydCkge1xuICAgICAgICAgICAgc3RhdGUgPSBHZW5TdGF0ZUNvbXBsZXRlZDtcbiAgICAgICAgICAgIHRocm93IGNvbnRleHQuYXJnO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIGNvbnRleHQuZGlzcGF0Y2hFeGNlcHRpb24oY29udGV4dC5hcmcpO1xuXG4gICAgICAgIH0gZWxzZSBpZiAoY29udGV4dC5tZXRob2QgPT09IFwicmV0dXJuXCIpIHtcbiAgICAgICAgICBjb250ZXh0LmFicnVwdChcInJldHVyblwiLCBjb250ZXh0LmFyZyk7XG4gICAgICAgIH1cblxuICAgICAgICBzdGF0ZSA9IEdlblN0YXRlRXhlY3V0aW5nO1xuXG4gICAgICAgIHZhciByZWNvcmQgPSB0cnlDYXRjaChpbm5lckZuLCBzZWxmLCBjb250ZXh0KTtcbiAgICAgICAgaWYgKHJlY29yZC50eXBlID09PSBcIm5vcm1hbFwiKSB7XG4gICAgICAgICAgLy8gSWYgYW4gZXhjZXB0aW9uIGlzIHRocm93biBmcm9tIGlubmVyRm4sIHdlIGxlYXZlIHN0YXRlID09PVxuICAgICAgICAgIC8vIEdlblN0YXRlRXhlY3V0aW5nIGFuZCBsb29wIGJhY2sgZm9yIGFub3RoZXIgaW52b2NhdGlvbi5cbiAgICAgICAgICBzdGF0ZSA9IGNvbnRleHQuZG9uZVxuICAgICAgICAgICAgPyBHZW5TdGF0ZUNvbXBsZXRlZFxuICAgICAgICAgICAgOiBHZW5TdGF0ZVN1c3BlbmRlZFlpZWxkO1xuXG4gICAgICAgICAgaWYgKHJlY29yZC5hcmcgPT09IENvbnRpbnVlU2VudGluZWwpIHtcbiAgICAgICAgICAgIGNvbnRpbnVlO1xuICAgICAgICAgIH1cblxuICAgICAgICAgIHJldHVybiB7XG4gICAgICAgICAgICB2YWx1ZTogcmVjb3JkLmFyZyxcbiAgICAgICAgICAgIGRvbmU6IGNvbnRleHQuZG9uZVxuICAgICAgICAgIH07XG5cbiAgICAgICAgfSBlbHNlIGlmIChyZWNvcmQudHlwZSA9PT0gXCJ0aHJvd1wiKSB7XG4gICAgICAgICAgc3RhdGUgPSBHZW5TdGF0ZUNvbXBsZXRlZDtcbiAgICAgICAgICAvLyBEaXNwYXRjaCB0aGUgZXhjZXB0aW9uIGJ5IGxvb3BpbmcgYmFjayBhcm91bmQgdG8gdGhlXG4gICAgICAgICAgLy8gY29udGV4dC5kaXNwYXRjaEV4Y2VwdGlvbihjb250ZXh0LmFyZykgY2FsbCBhYm92ZS5cbiAgICAgICAgICBjb250ZXh0Lm1ldGhvZCA9IFwidGhyb3dcIjtcbiAgICAgICAgICBjb250ZXh0LmFyZyA9IHJlY29yZC5hcmc7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9O1xuICB9XG5cbiAgLy8gQ2FsbCBkZWxlZ2F0ZS5pdGVyYXRvcltjb250ZXh0Lm1ldGhvZF0oY29udGV4dC5hcmcpIGFuZCBoYW5kbGUgdGhlXG4gIC8vIHJlc3VsdCwgZWl0aGVyIGJ5IHJldHVybmluZyBhIHsgdmFsdWUsIGRvbmUgfSByZXN1bHQgZnJvbSB0aGVcbiAgLy8gZGVsZWdhdGUgaXRlcmF0b3IsIG9yIGJ5IG1vZGlmeWluZyBjb250ZXh0Lm1ldGhvZCBhbmQgY29udGV4dC5hcmcsXG4gIC8vIHNldHRpbmcgY29udGV4dC5kZWxlZ2F0ZSB0byBudWxsLCBhbmQgcmV0dXJuaW5nIHRoZSBDb250aW51ZVNlbnRpbmVsLlxuICBmdW5jdGlvbiBtYXliZUludm9rZURlbGVnYXRlKGRlbGVnYXRlLCBjb250ZXh0KSB7XG4gICAgdmFyIG1ldGhvZCA9IGRlbGVnYXRlLml0ZXJhdG9yW2NvbnRleHQubWV0aG9kXTtcbiAgICBpZiAobWV0aG9kID09PSB1bmRlZmluZWQpIHtcbiAgICAgIC8vIEEgLnRocm93IG9yIC5yZXR1cm4gd2hlbiB0aGUgZGVsZWdhdGUgaXRlcmF0b3IgaGFzIG5vIC50aHJvd1xuICAgICAgLy8gbWV0aG9kIGFsd2F5cyB0ZXJtaW5hdGVzIHRoZSB5aWVsZCogbG9vcC5cbiAgICAgIGNvbnRleHQuZGVsZWdhdGUgPSBudWxsO1xuXG4gICAgICBpZiAoY29udGV4dC5tZXRob2QgPT09IFwidGhyb3dcIikge1xuICAgICAgICBpZiAoZGVsZWdhdGUuaXRlcmF0b3IucmV0dXJuKSB7XG4gICAgICAgICAgLy8gSWYgdGhlIGRlbGVnYXRlIGl0ZXJhdG9yIGhhcyBhIHJldHVybiBtZXRob2QsIGdpdmUgaXQgYVxuICAgICAgICAgIC8vIGNoYW5jZSB0byBjbGVhbiB1cC5cbiAgICAgICAgICBjb250ZXh0Lm1ldGhvZCA9IFwicmV0dXJuXCI7XG4gICAgICAgICAgY29udGV4dC5hcmcgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgbWF5YmVJbnZva2VEZWxlZ2F0ZShkZWxlZ2F0ZSwgY29udGV4dCk7XG5cbiAgICAgICAgICBpZiAoY29udGV4dC5tZXRob2QgPT09IFwidGhyb3dcIikge1xuICAgICAgICAgICAgLy8gSWYgbWF5YmVJbnZva2VEZWxlZ2F0ZShjb250ZXh0KSBjaGFuZ2VkIGNvbnRleHQubWV0aG9kIGZyb21cbiAgICAgICAgICAgIC8vIFwicmV0dXJuXCIgdG8gXCJ0aHJvd1wiLCBsZXQgdGhhdCBvdmVycmlkZSB0aGUgVHlwZUVycm9yIGJlbG93LlxuICAgICAgICAgICAgcmV0dXJuIENvbnRpbnVlU2VudGluZWw7XG4gICAgICAgICAgfVxuICAgICAgICB9XG5cbiAgICAgICAgY29udGV4dC5tZXRob2QgPSBcInRocm93XCI7XG4gICAgICAgIGNvbnRleHQuYXJnID0gbmV3IFR5cGVFcnJvcihcbiAgICAgICAgICBcIlRoZSBpdGVyYXRvciBkb2VzIG5vdCBwcm92aWRlIGEgJ3Rocm93JyBtZXRob2RcIik7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBDb250aW51ZVNlbnRpbmVsO1xuICAgIH1cblxuICAgIHZhciByZWNvcmQgPSB0cnlDYXRjaChtZXRob2QsIGRlbGVnYXRlLml0ZXJhdG9yLCBjb250ZXh0LmFyZyk7XG5cbiAgICBpZiAocmVjb3JkLnR5cGUgPT09IFwidGhyb3dcIikge1xuICAgICAgY29udGV4dC5tZXRob2QgPSBcInRocm93XCI7XG4gICAgICBjb250ZXh0LmFyZyA9IHJlY29yZC5hcmc7XG4gICAgICBjb250ZXh0LmRlbGVnYXRlID0gbnVsbDtcbiAgICAgIHJldHVybiBDb250aW51ZVNlbnRpbmVsO1xuICAgIH1cblxuICAgIHZhciBpbmZvID0gcmVjb3JkLmFyZztcblxuICAgIGlmICghIGluZm8pIHtcbiAgICAgIGNvbnRleHQubWV0aG9kID0gXCJ0aHJvd1wiO1xuICAgICAgY29udGV4dC5hcmcgPSBuZXcgVHlwZUVycm9yKFwiaXRlcmF0b3IgcmVzdWx0IGlzIG5vdCBhbiBvYmplY3RcIik7XG4gICAgICBjb250ZXh0LmRlbGVnYXRlID0gbnVsbDtcbiAgICAgIHJldHVybiBDb250aW51ZVNlbnRpbmVsO1xuICAgIH1cblxuICAgIGlmIChpbmZvLmRvbmUpIHtcbiAgICAgIC8vIEFzc2lnbiB0aGUgcmVzdWx0IG9mIHRoZSBmaW5pc2hlZCBkZWxlZ2F0ZSB0byB0aGUgdGVtcG9yYXJ5XG4gICAgICAvLyB2YXJpYWJsZSBzcGVjaWZpZWQgYnkgZGVsZWdhdGUucmVzdWx0TmFtZSAoc2VlIGRlbGVnYXRlWWllbGQpLlxuICAgICAgY29udGV4dFtkZWxlZ2F0ZS5yZXN1bHROYW1lXSA9IGluZm8udmFsdWU7XG5cbiAgICAgIC8vIFJlc3VtZSBleGVjdXRpb24gYXQgdGhlIGRlc2lyZWQgbG9jYXRpb24gKHNlZSBkZWxlZ2F0ZVlpZWxkKS5cbiAgICAgIGNvbnRleHQubmV4dCA9IGRlbGVnYXRlLm5leHRMb2M7XG5cbiAgICAgIC8vIElmIGNvbnRleHQubWV0aG9kIHdhcyBcInRocm93XCIgYnV0IHRoZSBkZWxlZ2F0ZSBoYW5kbGVkIHRoZVxuICAgICAgLy8gZXhjZXB0aW9uLCBsZXQgdGhlIG91dGVyIGdlbmVyYXRvciBwcm9jZWVkIG5vcm1hbGx5LiBJZlxuICAgICAgLy8gY29udGV4dC5tZXRob2Qgd2FzIFwibmV4dFwiLCBmb3JnZXQgY29udGV4dC5hcmcgc2luY2UgaXQgaGFzIGJlZW5cbiAgICAgIC8vIFwiY29uc3VtZWRcIiBieSB0aGUgZGVsZWdhdGUgaXRlcmF0b3IuIElmIGNvbnRleHQubWV0aG9kIHdhc1xuICAgICAgLy8gXCJyZXR1cm5cIiwgYWxsb3cgdGhlIG9yaWdpbmFsIC5yZXR1cm4gY2FsbCB0byBjb250aW51ZSBpbiB0aGVcbiAgICAgIC8vIG91dGVyIGdlbmVyYXRvci5cbiAgICAgIGlmIChjb250ZXh0Lm1ldGhvZCAhPT0gXCJyZXR1cm5cIikge1xuICAgICAgICBjb250ZXh0Lm1ldGhvZCA9IFwibmV4dFwiO1xuICAgICAgICBjb250ZXh0LmFyZyA9IHVuZGVmaW5lZDtcbiAgICAgIH1cblxuICAgIH0gZWxzZSB7XG4gICAgICAvLyBSZS15aWVsZCB0aGUgcmVzdWx0IHJldHVybmVkIGJ5IHRoZSBkZWxlZ2F0ZSBtZXRob2QuXG4gICAgICByZXR1cm4gaW5mbztcbiAgICB9XG5cbiAgICAvLyBUaGUgZGVsZWdhdGUgaXRlcmF0b3IgaXMgZmluaXNoZWQsIHNvIGZvcmdldCBpdCBhbmQgY29udGludWUgd2l0aFxuICAgIC8vIHRoZSBvdXRlciBnZW5lcmF0b3IuXG4gICAgY29udGV4dC5kZWxlZ2F0ZSA9IG51bGw7XG4gICAgcmV0dXJuIENvbnRpbnVlU2VudGluZWw7XG4gIH1cblxuICAvLyBEZWZpbmUgR2VuZXJhdG9yLnByb3RvdHlwZS57bmV4dCx0aHJvdyxyZXR1cm59IGluIHRlcm1zIG9mIHRoZVxuICAvLyB1bmlmaWVkIC5faW52b2tlIGhlbHBlciBtZXRob2QuXG4gIGRlZmluZUl0ZXJhdG9yTWV0aG9kcyhHcCk7XG5cbiAgR3BbdG9TdHJpbmdUYWdTeW1ib2xdID0gXCJHZW5lcmF0b3JcIjtcblxuICAvLyBBIEdlbmVyYXRvciBzaG91bGQgYWx3YXlzIHJldHVybiBpdHNlbGYgYXMgdGhlIGl0ZXJhdG9yIG9iamVjdCB3aGVuIHRoZVxuICAvLyBAQGl0ZXJhdG9yIGZ1bmN0aW9uIGlzIGNhbGxlZCBvbiBpdC4gU29tZSBicm93c2VycycgaW1wbGVtZW50YXRpb25zIG9mIHRoZVxuICAvLyBpdGVyYXRvciBwcm90b3R5cGUgY2hhaW4gaW5jb3JyZWN0bHkgaW1wbGVtZW50IHRoaXMsIGNhdXNpbmcgdGhlIEdlbmVyYXRvclxuICAvLyBvYmplY3QgdG8gbm90IGJlIHJldHVybmVkIGZyb20gdGhpcyBjYWxsLiBUaGlzIGVuc3VyZXMgdGhhdCBkb2Vzbid0IGhhcHBlbi5cbiAgLy8gU2VlIGh0dHBzOi8vZ2l0aHViLmNvbS9mYWNlYm9vay9yZWdlbmVyYXRvci9pc3N1ZXMvMjc0IGZvciBtb3JlIGRldGFpbHMuXG4gIEdwW2l0ZXJhdG9yU3ltYm9sXSA9IGZ1bmN0aW9uKCkge1xuICAgIHJldHVybiB0aGlzO1xuICB9O1xuXG4gIEdwLnRvU3RyaW5nID0gZnVuY3Rpb24oKSB7XG4gICAgcmV0dXJuIFwiW29iamVjdCBHZW5lcmF0b3JdXCI7XG4gIH07XG5cbiAgZnVuY3Rpb24gcHVzaFRyeUVudHJ5KGxvY3MpIHtcbiAgICB2YXIgZW50cnkgPSB7IHRyeUxvYzogbG9jc1swXSB9O1xuXG4gICAgaWYgKDEgaW4gbG9jcykge1xuICAgICAgZW50cnkuY2F0Y2hMb2MgPSBsb2NzWzFdO1xuICAgIH1cblxuICAgIGlmICgyIGluIGxvY3MpIHtcbiAgICAgIGVudHJ5LmZpbmFsbHlMb2MgPSBsb2NzWzJdO1xuICAgICAgZW50cnkuYWZ0ZXJMb2MgPSBsb2NzWzNdO1xuICAgIH1cblxuICAgIHRoaXMudHJ5RW50cmllcy5wdXNoKGVudHJ5KTtcbiAgfVxuXG4gIGZ1bmN0aW9uIHJlc2V0VHJ5RW50cnkoZW50cnkpIHtcbiAgICB2YXIgcmVjb3JkID0gZW50cnkuY29tcGxldGlvbiB8fCB7fTtcbiAgICByZWNvcmQudHlwZSA9IFwibm9ybWFsXCI7XG4gICAgZGVsZXRlIHJlY29yZC5hcmc7XG4gICAgZW50cnkuY29tcGxldGlvbiA9IHJlY29yZDtcbiAgfVxuXG4gIGZ1bmN0aW9uIENvbnRleHQodHJ5TG9jc0xpc3QpIHtcbiAgICAvLyBUaGUgcm9vdCBlbnRyeSBvYmplY3QgKGVmZmVjdGl2ZWx5IGEgdHJ5IHN0YXRlbWVudCB3aXRob3V0IGEgY2F0Y2hcbiAgICAvLyBvciBhIGZpbmFsbHkgYmxvY2spIGdpdmVzIHVzIGEgcGxhY2UgdG8gc3RvcmUgdmFsdWVzIHRocm93biBmcm9tXG4gICAgLy8gbG9jYXRpb25zIHdoZXJlIHRoZXJlIGlzIG5vIGVuY2xvc2luZyB0cnkgc3RhdGVtZW50LlxuICAgIHRoaXMudHJ5RW50cmllcyA9IFt7IHRyeUxvYzogXCJyb290XCIgfV07XG4gICAgdHJ5TG9jc0xpc3QuZm9yRWFjaChwdXNoVHJ5RW50cnksIHRoaXMpO1xuICAgIHRoaXMucmVzZXQodHJ1ZSk7XG4gIH1cblxuICBydW50aW1lLmtleXMgPSBmdW5jdGlvbihvYmplY3QpIHtcbiAgICB2YXIga2V5cyA9IFtdO1xuICAgIGZvciAodmFyIGtleSBpbiBvYmplY3QpIHtcbiAgICAgIGtleXMucHVzaChrZXkpO1xuICAgIH1cbiAgICBrZXlzLnJldmVyc2UoKTtcblxuICAgIC8vIFJhdGhlciB0aGFuIHJldHVybmluZyBhbiBvYmplY3Qgd2l0aCBhIG5leHQgbWV0aG9kLCB3ZSBrZWVwXG4gICAgLy8gdGhpbmdzIHNpbXBsZSBhbmQgcmV0dXJuIHRoZSBuZXh0IGZ1bmN0aW9uIGl0c2VsZi5cbiAgICByZXR1cm4gZnVuY3Rpb24gbmV4dCgpIHtcbiAgICAgIHdoaWxlIChrZXlzLmxlbmd0aCkge1xuICAgICAgICB2YXIga2V5ID0ga2V5cy5wb3AoKTtcbiAgICAgICAgaWYgKGtleSBpbiBvYmplY3QpIHtcbiAgICAgICAgICBuZXh0LnZhbHVlID0ga2V5O1xuICAgICAgICAgIG5leHQuZG9uZSA9IGZhbHNlO1xuICAgICAgICAgIHJldHVybiBuZXh0O1xuICAgICAgICB9XG4gICAgICB9XG5cbiAgICAgIC8vIFRvIGF2b2lkIGNyZWF0aW5nIGFuIGFkZGl0aW9uYWwgb2JqZWN0LCB3ZSBqdXN0IGhhbmcgdGhlIC52YWx1ZVxuICAgICAgLy8gYW5kIC5kb25lIHByb3BlcnRpZXMgb2ZmIHRoZSBuZXh0IGZ1bmN0aW9uIG9iamVjdCBpdHNlbGYuIFRoaXNcbiAgICAgIC8vIGFsc28gZW5zdXJlcyB0aGF0IHRoZSBtaW5pZmllciB3aWxsIG5vdCBhbm9ueW1pemUgdGhlIGZ1bmN0aW9uLlxuICAgICAgbmV4dC5kb25lID0gdHJ1ZTtcbiAgICAgIHJldHVybiBuZXh0O1xuICAgIH07XG4gIH07XG5cbiAgZnVuY3Rpb24gdmFsdWVzKGl0ZXJhYmxlKSB7XG4gICAgaWYgKGl0ZXJhYmxlKSB7XG4gICAgICB2YXIgaXRlcmF0b3JNZXRob2QgPSBpdGVyYWJsZVtpdGVyYXRvclN5bWJvbF07XG4gICAgICBpZiAoaXRlcmF0b3JNZXRob2QpIHtcbiAgICAgICAgcmV0dXJuIGl0ZXJhdG9yTWV0aG9kLmNhbGwoaXRlcmFibGUpO1xuICAgICAgfVxuXG4gICAgICBpZiAodHlwZW9mIGl0ZXJhYmxlLm5leHQgPT09IFwiZnVuY3Rpb25cIikge1xuICAgICAgICByZXR1cm4gaXRlcmFibGU7XG4gICAgICB9XG5cbiAgICAgIGlmICghaXNOYU4oaXRlcmFibGUubGVuZ3RoKSkge1xuICAgICAgICB2YXIgaSA9IC0xLCBuZXh0ID0gZnVuY3Rpb24gbmV4dCgpIHtcbiAgICAgICAgICB3aGlsZSAoKytpIDwgaXRlcmFibGUubGVuZ3RoKSB7XG4gICAgICAgICAgICBpZiAoaGFzT3duLmNhbGwoaXRlcmFibGUsIGkpKSB7XG4gICAgICAgICAgICAgIG5leHQudmFsdWUgPSBpdGVyYWJsZVtpXTtcbiAgICAgICAgICAgICAgbmV4dC5kb25lID0gZmFsc2U7XG4gICAgICAgICAgICAgIHJldHVybiBuZXh0O1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cblxuICAgICAgICAgIG5leHQudmFsdWUgPSB1bmRlZmluZWQ7XG4gICAgICAgICAgbmV4dC5kb25lID0gdHJ1ZTtcblxuICAgICAgICAgIHJldHVybiBuZXh0O1xuICAgICAgICB9O1xuXG4gICAgICAgIHJldHVybiBuZXh0Lm5leHQgPSBuZXh0O1xuICAgICAgfVxuICAgIH1cblxuICAgIC8vIFJldHVybiBhbiBpdGVyYXRvciB3aXRoIG5vIHZhbHVlcy5cbiAgICByZXR1cm4geyBuZXh0OiBkb25lUmVzdWx0IH07XG4gIH1cbiAgcnVudGltZS52YWx1ZXMgPSB2YWx1ZXM7XG5cbiAgZnVuY3Rpb24gZG9uZVJlc3VsdCgpIHtcbiAgICByZXR1cm4geyB2YWx1ZTogdW5kZWZpbmVkLCBkb25lOiB0cnVlIH07XG4gIH1cblxuICBDb250ZXh0LnByb3RvdHlwZSA9IHtcbiAgICBjb25zdHJ1Y3RvcjogQ29udGV4dCxcblxuICAgIHJlc2V0OiBmdW5jdGlvbihza2lwVGVtcFJlc2V0KSB7XG4gICAgICB0aGlzLnByZXYgPSAwO1xuICAgICAgdGhpcy5uZXh0ID0gMDtcbiAgICAgIC8vIFJlc2V0dGluZyBjb250ZXh0Ll9zZW50IGZvciBsZWdhY3kgc3VwcG9ydCBvZiBCYWJlbCdzXG4gICAgICAvLyBmdW5jdGlvbi5zZW50IGltcGxlbWVudGF0aW9uLlxuICAgICAgdGhpcy5zZW50ID0gdGhpcy5fc2VudCA9IHVuZGVmaW5lZDtcbiAgICAgIHRoaXMuZG9uZSA9IGZhbHNlO1xuICAgICAgdGhpcy5kZWxlZ2F0ZSA9IG51bGw7XG5cbiAgICAgIHRoaXMubWV0aG9kID0gXCJuZXh0XCI7XG4gICAgICB0aGlzLmFyZyA9IHVuZGVmaW5lZDtcblxuICAgICAgdGhpcy50cnlFbnRyaWVzLmZvckVhY2gocmVzZXRUcnlFbnRyeSk7XG5cbiAgICAgIGlmICghc2tpcFRlbXBSZXNldCkge1xuICAgICAgICBmb3IgKHZhciBuYW1lIGluIHRoaXMpIHtcbiAgICAgICAgICAvLyBOb3Qgc3VyZSBhYm91dCB0aGUgb3B0aW1hbCBvcmRlciBvZiB0aGVzZSBjb25kaXRpb25zOlxuICAgICAgICAgIGlmIChuYW1lLmNoYXJBdCgwKSA9PT0gXCJ0XCIgJiZcbiAgICAgICAgICAgICAgaGFzT3duLmNhbGwodGhpcywgbmFtZSkgJiZcbiAgICAgICAgICAgICAgIWlzTmFOKCtuYW1lLnNsaWNlKDEpKSkge1xuICAgICAgICAgICAgdGhpc1tuYW1lXSA9IHVuZGVmaW5lZDtcbiAgICAgICAgICB9XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuXG4gICAgc3RvcDogZnVuY3Rpb24oKSB7XG4gICAgICB0aGlzLmRvbmUgPSB0cnVlO1xuXG4gICAgICB2YXIgcm9vdEVudHJ5ID0gdGhpcy50cnlFbnRyaWVzWzBdO1xuICAgICAgdmFyIHJvb3RSZWNvcmQgPSByb290RW50cnkuY29tcGxldGlvbjtcbiAgICAgIGlmIChyb290UmVjb3JkLnR5cGUgPT09IFwidGhyb3dcIikge1xuICAgICAgICB0aHJvdyByb290UmVjb3JkLmFyZztcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHRoaXMucnZhbDtcbiAgICB9LFxuXG4gICAgZGlzcGF0Y2hFeGNlcHRpb246IGZ1bmN0aW9uKGV4Y2VwdGlvbikge1xuICAgICAgaWYgKHRoaXMuZG9uZSkge1xuICAgICAgICB0aHJvdyBleGNlcHRpb247XG4gICAgICB9XG5cbiAgICAgIHZhciBjb250ZXh0ID0gdGhpcztcbiAgICAgIGZ1bmN0aW9uIGhhbmRsZShsb2MsIGNhdWdodCkge1xuICAgICAgICByZWNvcmQudHlwZSA9IFwidGhyb3dcIjtcbiAgICAgICAgcmVjb3JkLmFyZyA9IGV4Y2VwdGlvbjtcbiAgICAgICAgY29udGV4dC5uZXh0ID0gbG9jO1xuXG4gICAgICAgIGlmIChjYXVnaHQpIHtcbiAgICAgICAgICAvLyBJZiB0aGUgZGlzcGF0Y2hlZCBleGNlcHRpb24gd2FzIGNhdWdodCBieSBhIGNhdGNoIGJsb2NrLFxuICAgICAgICAgIC8vIHRoZW4gbGV0IHRoYXQgY2F0Y2ggYmxvY2sgaGFuZGxlIHRoZSBleGNlcHRpb24gbm9ybWFsbHkuXG4gICAgICAgICAgY29udGV4dC5tZXRob2QgPSBcIm5leHRcIjtcbiAgICAgICAgICBjb250ZXh0LmFyZyA9IHVuZGVmaW5lZDtcbiAgICAgICAgfVxuXG4gICAgICAgIHJldHVybiAhISBjYXVnaHQ7XG4gICAgICB9XG5cbiAgICAgIGZvciAodmFyIGkgPSB0aGlzLnRyeUVudHJpZXMubGVuZ3RoIC0gMTsgaSA+PSAwOyAtLWkpIHtcbiAgICAgICAgdmFyIGVudHJ5ID0gdGhpcy50cnlFbnRyaWVzW2ldO1xuICAgICAgICB2YXIgcmVjb3JkID0gZW50cnkuY29tcGxldGlvbjtcblxuICAgICAgICBpZiAoZW50cnkudHJ5TG9jID09PSBcInJvb3RcIikge1xuICAgICAgICAgIC8vIEV4Y2VwdGlvbiB0aHJvd24gb3V0c2lkZSBvZiBhbnkgdHJ5IGJsb2NrIHRoYXQgY291bGQgaGFuZGxlXG4gICAgICAgICAgLy8gaXQsIHNvIHNldCB0aGUgY29tcGxldGlvbiB2YWx1ZSBvZiB0aGUgZW50aXJlIGZ1bmN0aW9uIHRvXG4gICAgICAgICAgLy8gdGhyb3cgdGhlIGV4Y2VwdGlvbi5cbiAgICAgICAgICByZXR1cm4gaGFuZGxlKFwiZW5kXCIpO1xuICAgICAgICB9XG5cbiAgICAgICAgaWYgKGVudHJ5LnRyeUxvYyA8PSB0aGlzLnByZXYpIHtcbiAgICAgICAgICB2YXIgaGFzQ2F0Y2ggPSBoYXNPd24uY2FsbChlbnRyeSwgXCJjYXRjaExvY1wiKTtcbiAgICAgICAgICB2YXIgaGFzRmluYWxseSA9IGhhc093bi5jYWxsKGVudHJ5LCBcImZpbmFsbHlMb2NcIik7XG5cbiAgICAgICAgICBpZiAoaGFzQ2F0Y2ggJiYgaGFzRmluYWxseSkge1xuICAgICAgICAgICAgaWYgKHRoaXMucHJldiA8IGVudHJ5LmNhdGNoTG9jKSB7XG4gICAgICAgICAgICAgIHJldHVybiBoYW5kbGUoZW50cnkuY2F0Y2hMb2MsIHRydWUpO1xuICAgICAgICAgICAgfSBlbHNlIGlmICh0aGlzLnByZXYgPCBlbnRyeS5maW5hbGx5TG9jKSB7XG4gICAgICAgICAgICAgIHJldHVybiBoYW5kbGUoZW50cnkuZmluYWxseUxvYyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICB9IGVsc2UgaWYgKGhhc0NhdGNoKSB7XG4gICAgICAgICAgICBpZiAodGhpcy5wcmV2IDwgZW50cnkuY2F0Y2hMb2MpIHtcbiAgICAgICAgICAgICAgcmV0dXJuIGhhbmRsZShlbnRyeS5jYXRjaExvYywgdHJ1ZSk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICB9IGVsc2UgaWYgKGhhc0ZpbmFsbHkpIHtcbiAgICAgICAgICAgIGlmICh0aGlzLnByZXYgPCBlbnRyeS5maW5hbGx5TG9jKSB7XG4gICAgICAgICAgICAgIHJldHVybiBoYW5kbGUoZW50cnkuZmluYWxseUxvYyk7XG4gICAgICAgICAgICB9XG5cbiAgICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwidHJ5IHN0YXRlbWVudCB3aXRob3V0IGNhdGNoIG9yIGZpbmFsbHlcIik7XG4gICAgICAgICAgfVxuICAgICAgICB9XG4gICAgICB9XG4gICAgfSxcblxuICAgIGFicnVwdDogZnVuY3Rpb24odHlwZSwgYXJnKSB7XG4gICAgICBmb3IgKHZhciBpID0gdGhpcy50cnlFbnRyaWVzLmxlbmd0aCAtIDE7IGkgPj0gMDsgLS1pKSB7XG4gICAgICAgIHZhciBlbnRyeSA9IHRoaXMudHJ5RW50cmllc1tpXTtcbiAgICAgICAgaWYgKGVudHJ5LnRyeUxvYyA8PSB0aGlzLnByZXYgJiZcbiAgICAgICAgICAgIGhhc093bi5jYWxsKGVudHJ5LCBcImZpbmFsbHlMb2NcIikgJiZcbiAgICAgICAgICAgIHRoaXMucHJldiA8IGVudHJ5LmZpbmFsbHlMb2MpIHtcbiAgICAgICAgICB2YXIgZmluYWxseUVudHJ5ID0gZW50cnk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIH1cbiAgICAgIH1cblxuICAgICAgaWYgKGZpbmFsbHlFbnRyeSAmJlxuICAgICAgICAgICh0eXBlID09PSBcImJyZWFrXCIgfHxcbiAgICAgICAgICAgdHlwZSA9PT0gXCJjb250aW51ZVwiKSAmJlxuICAgICAgICAgIGZpbmFsbHlFbnRyeS50cnlMb2MgPD0gYXJnICYmXG4gICAgICAgICAgYXJnIDw9IGZpbmFsbHlFbnRyeS5maW5hbGx5TG9jKSB7XG4gICAgICAgIC8vIElnbm9yZSB0aGUgZmluYWxseSBlbnRyeSBpZiBjb250cm9sIGlzIG5vdCBqdW1waW5nIHRvIGFcbiAgICAgICAgLy8gbG9jYXRpb24gb3V0c2lkZSB0aGUgdHJ5L2NhdGNoIGJsb2NrLlxuICAgICAgICBmaW5hbGx5RW50cnkgPSBudWxsO1xuICAgICAgfVxuXG4gICAgICB2YXIgcmVjb3JkID0gZmluYWxseUVudHJ5ID8gZmluYWxseUVudHJ5LmNvbXBsZXRpb24gOiB7fTtcbiAgICAgIHJlY29yZC50eXBlID0gdHlwZTtcbiAgICAgIHJlY29yZC5hcmcgPSBhcmc7XG5cbiAgICAgIGlmIChmaW5hbGx5RW50cnkpIHtcbiAgICAgICAgdGhpcy5tZXRob2QgPSBcIm5leHRcIjtcbiAgICAgICAgdGhpcy5uZXh0ID0gZmluYWxseUVudHJ5LmZpbmFsbHlMb2M7XG4gICAgICAgIHJldHVybiBDb250aW51ZVNlbnRpbmVsO1xuICAgICAgfVxuXG4gICAgICByZXR1cm4gdGhpcy5jb21wbGV0ZShyZWNvcmQpO1xuICAgIH0sXG5cbiAgICBjb21wbGV0ZTogZnVuY3Rpb24ocmVjb3JkLCBhZnRlckxvYykge1xuICAgICAgaWYgKHJlY29yZC50eXBlID09PSBcInRocm93XCIpIHtcbiAgICAgICAgdGhyb3cgcmVjb3JkLmFyZztcbiAgICAgIH1cblxuICAgICAgaWYgKHJlY29yZC50eXBlID09PSBcImJyZWFrXCIgfHxcbiAgICAgICAgICByZWNvcmQudHlwZSA9PT0gXCJjb250aW51ZVwiKSB7XG4gICAgICAgIHRoaXMubmV4dCA9IHJlY29yZC5hcmc7XG4gICAgICB9IGVsc2UgaWYgKHJlY29yZC50eXBlID09PSBcInJldHVyblwiKSB7XG4gICAgICAgIHRoaXMucnZhbCA9IHRoaXMuYXJnID0gcmVjb3JkLmFyZztcbiAgICAgICAgdGhpcy5tZXRob2QgPSBcInJldHVyblwiO1xuICAgICAgICB0aGlzLm5leHQgPSBcImVuZFwiO1xuICAgICAgfSBlbHNlIGlmIChyZWNvcmQudHlwZSA9PT0gXCJub3JtYWxcIiAmJiBhZnRlckxvYykge1xuICAgICAgICB0aGlzLm5leHQgPSBhZnRlckxvYztcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIENvbnRpbnVlU2VudGluZWw7XG4gICAgfSxcblxuICAgIGZpbmlzaDogZnVuY3Rpb24oZmluYWxseUxvYykge1xuICAgICAgZm9yICh2YXIgaSA9IHRoaXMudHJ5RW50cmllcy5sZW5ndGggLSAxOyBpID49IDA7IC0taSkge1xuICAgICAgICB2YXIgZW50cnkgPSB0aGlzLnRyeUVudHJpZXNbaV07XG4gICAgICAgIGlmIChlbnRyeS5maW5hbGx5TG9jID09PSBmaW5hbGx5TG9jKSB7XG4gICAgICAgICAgdGhpcy5jb21wbGV0ZShlbnRyeS5jb21wbGV0aW9uLCBlbnRyeS5hZnRlckxvYyk7XG4gICAgICAgICAgcmVzZXRUcnlFbnRyeShlbnRyeSk7XG4gICAgICAgICAgcmV0dXJuIENvbnRpbnVlU2VudGluZWw7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9LFxuXG4gICAgXCJjYXRjaFwiOiBmdW5jdGlvbih0cnlMb2MpIHtcbiAgICAgIGZvciAodmFyIGkgPSB0aGlzLnRyeUVudHJpZXMubGVuZ3RoIC0gMTsgaSA+PSAwOyAtLWkpIHtcbiAgICAgICAgdmFyIGVudHJ5ID0gdGhpcy50cnlFbnRyaWVzW2ldO1xuICAgICAgICBpZiAoZW50cnkudHJ5TG9jID09PSB0cnlMb2MpIHtcbiAgICAgICAgICB2YXIgcmVjb3JkID0gZW50cnkuY29tcGxldGlvbjtcbiAgICAgICAgICBpZiAocmVjb3JkLnR5cGUgPT09IFwidGhyb3dcIikge1xuICAgICAgICAgICAgdmFyIHRocm93biA9IHJlY29yZC5hcmc7XG4gICAgICAgICAgICByZXNldFRyeUVudHJ5KGVudHJ5KTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHRocm93bjtcbiAgICAgICAgfVxuICAgICAgfVxuXG4gICAgICAvLyBUaGUgY29udGV4dC5jYXRjaCBtZXRob2QgbXVzdCBvbmx5IGJlIGNhbGxlZCB3aXRoIGEgbG9jYXRpb25cbiAgICAgIC8vIGFyZ3VtZW50IHRoYXQgY29ycmVzcG9uZHMgdG8gYSBrbm93biBjYXRjaCBibG9jay5cbiAgICAgIHRocm93IG5ldyBFcnJvcihcImlsbGVnYWwgY2F0Y2ggYXR0ZW1wdFwiKTtcbiAgICB9LFxuXG4gICAgZGVsZWdhdGVZaWVsZDogZnVuY3Rpb24oaXRlcmFibGUsIHJlc3VsdE5hbWUsIG5leHRMb2MpIHtcbiAgICAgIHRoaXMuZGVsZWdhdGUgPSB7XG4gICAgICAgIGl0ZXJhdG9yOiB2YWx1ZXMoaXRlcmFibGUpLFxuICAgICAgICByZXN1bHROYW1lOiByZXN1bHROYW1lLFxuICAgICAgICBuZXh0TG9jOiBuZXh0TG9jXG4gICAgICB9O1xuXG4gICAgICBpZiAodGhpcy5tZXRob2QgPT09IFwibmV4dFwiKSB7XG4gICAgICAgIC8vIERlbGliZXJhdGVseSBmb3JnZXQgdGhlIGxhc3Qgc2VudCB2YWx1ZSBzbyB0aGF0IHdlIGRvbid0XG4gICAgICAgIC8vIGFjY2lkZW50YWxseSBwYXNzIGl0IG9uIHRvIHRoZSBkZWxlZ2F0ZS5cbiAgICAgICAgdGhpcy5hcmcgPSB1bmRlZmluZWQ7XG4gICAgICB9XG5cbiAgICAgIHJldHVybiBDb250aW51ZVNlbnRpbmVsO1xuICAgIH1cbiAgfTtcbn0pKFxuICAvLyBBbW9uZyB0aGUgdmFyaW91cyB0cmlja3MgZm9yIG9idGFpbmluZyBhIHJlZmVyZW5jZSB0byB0aGUgZ2xvYmFsXG4gIC8vIG9iamVjdCwgdGhpcyBzZWVtcyB0byBiZSB0aGUgbW9zdCByZWxpYWJsZSB0ZWNobmlxdWUgdGhhdCBkb2VzIG5vdFxuICAvLyB1c2UgaW5kaXJlY3QgZXZhbCAod2hpY2ggdmlvbGF0ZXMgQ29udGVudCBTZWN1cml0eSBQb2xpY3kpLlxuICB0eXBlb2YgZ2xvYmFsID09PSBcIm9iamVjdFwiID8gZ2xvYmFsIDpcbiAgdHlwZW9mIHdpbmRvdyA9PT0gXCJvYmplY3RcIiA/IHdpbmRvdyA6XG4gIHR5cGVvZiBzZWxmID09PSBcIm9iamVjdFwiID8gc2VsZiA6IHRoaXNcbik7XG4iLCIndXNlIHN0cmljdCdcblxuZXhwb3J0cy5ieXRlTGVuZ3RoID0gYnl0ZUxlbmd0aFxuZXhwb3J0cy50b0J5dGVBcnJheSA9IHRvQnl0ZUFycmF5XG5leHBvcnRzLmZyb21CeXRlQXJyYXkgPSBmcm9tQnl0ZUFycmF5XG5cbnZhciBsb29rdXAgPSBbXVxudmFyIHJldkxvb2t1cCA9IFtdXG52YXIgQXJyID0gdHlwZW9mIFVpbnQ4QXJyYXkgIT09ICd1bmRlZmluZWQnID8gVWludDhBcnJheSA6IEFycmF5XG5cbnZhciBjb2RlID0gJ0FCQ0RFRkdISUpLTE1OT1BRUlNUVVZXWFlaYWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXowMTIzNDU2Nzg5Ky8nXG5mb3IgKHZhciBpID0gMCwgbGVuID0gY29kZS5sZW5ndGg7IGkgPCBsZW47ICsraSkge1xuICBsb29rdXBbaV0gPSBjb2RlW2ldXG4gIHJldkxvb2t1cFtjb2RlLmNoYXJDb2RlQXQoaSldID0gaVxufVxuXG4vLyBTdXBwb3J0IGRlY29kaW5nIFVSTC1zYWZlIGJhc2U2NCBzdHJpbmdzLCBhcyBOb2RlLmpzIGRvZXMuXG4vLyBTZWU6IGh0dHBzOi8vZW4ud2lraXBlZGlhLm9yZy93aWtpL0Jhc2U2NCNVUkxfYXBwbGljYXRpb25zXG5yZXZMb29rdXBbJy0nLmNoYXJDb2RlQXQoMCldID0gNjJcbnJldkxvb2t1cFsnXycuY2hhckNvZGVBdCgwKV0gPSA2M1xuXG5mdW5jdGlvbiBnZXRMZW5zIChiNjQpIHtcbiAgdmFyIGxlbiA9IGI2NC5sZW5ndGhcblxuICBpZiAobGVuICUgNCA+IDApIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoJ0ludmFsaWQgc3RyaW5nLiBMZW5ndGggbXVzdCBiZSBhIG11bHRpcGxlIG9mIDQnKVxuICB9XG5cbiAgLy8gVHJpbSBvZmYgZXh0cmEgYnl0ZXMgYWZ0ZXIgcGxhY2Vob2xkZXIgYnl0ZXMgYXJlIGZvdW5kXG4gIC8vIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL2JlYXRnYW1taXQvYmFzZTY0LWpzL2lzc3Vlcy80MlxuICB2YXIgdmFsaWRMZW4gPSBiNjQuaW5kZXhPZignPScpXG4gIGlmICh2YWxpZExlbiA9PT0gLTEpIHZhbGlkTGVuID0gbGVuXG5cbiAgdmFyIHBsYWNlSG9sZGVyc0xlbiA9IHZhbGlkTGVuID09PSBsZW5cbiAgICA/IDBcbiAgICA6IDQgLSAodmFsaWRMZW4gJSA0KVxuXG4gIHJldHVybiBbdmFsaWRMZW4sIHBsYWNlSG9sZGVyc0xlbl1cbn1cblxuLy8gYmFzZTY0IGlzIDQvMyArIHVwIHRvIHR3byBjaGFyYWN0ZXJzIG9mIHRoZSBvcmlnaW5hbCBkYXRhXG5mdW5jdGlvbiBieXRlTGVuZ3RoIChiNjQpIHtcbiAgdmFyIGxlbnMgPSBnZXRMZW5zKGI2NClcbiAgdmFyIHZhbGlkTGVuID0gbGVuc1swXVxuICB2YXIgcGxhY2VIb2xkZXJzTGVuID0gbGVuc1sxXVxuICByZXR1cm4gKCh2YWxpZExlbiArIHBsYWNlSG9sZGVyc0xlbikgKiAzIC8gNCkgLSBwbGFjZUhvbGRlcnNMZW5cbn1cblxuZnVuY3Rpb24gX2J5dGVMZW5ndGggKGI2NCwgdmFsaWRMZW4sIHBsYWNlSG9sZGVyc0xlbikge1xuICByZXR1cm4gKCh2YWxpZExlbiArIHBsYWNlSG9sZGVyc0xlbikgKiAzIC8gNCkgLSBwbGFjZUhvbGRlcnNMZW5cbn1cblxuZnVuY3Rpb24gdG9CeXRlQXJyYXkgKGI2NCkge1xuICB2YXIgdG1wXG4gIHZhciBsZW5zID0gZ2V0TGVucyhiNjQpXG4gIHZhciB2YWxpZExlbiA9IGxlbnNbMF1cbiAgdmFyIHBsYWNlSG9sZGVyc0xlbiA9IGxlbnNbMV1cblxuICB2YXIgYXJyID0gbmV3IEFycihfYnl0ZUxlbmd0aChiNjQsIHZhbGlkTGVuLCBwbGFjZUhvbGRlcnNMZW4pKVxuXG4gIHZhciBjdXJCeXRlID0gMFxuXG4gIC8vIGlmIHRoZXJlIGFyZSBwbGFjZWhvbGRlcnMsIG9ubHkgZ2V0IHVwIHRvIHRoZSBsYXN0IGNvbXBsZXRlIDQgY2hhcnNcbiAgdmFyIGxlbiA9IHBsYWNlSG9sZGVyc0xlbiA+IDBcbiAgICA/IHZhbGlkTGVuIC0gNFxuICAgIDogdmFsaWRMZW5cblxuICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbjsgaSArPSA0KSB7XG4gICAgdG1wID1cbiAgICAgIChyZXZMb29rdXBbYjY0LmNoYXJDb2RlQXQoaSldIDw8IDE4KSB8XG4gICAgICAocmV2TG9va3VwW2I2NC5jaGFyQ29kZUF0KGkgKyAxKV0gPDwgMTIpIHxcbiAgICAgIChyZXZMb29rdXBbYjY0LmNoYXJDb2RlQXQoaSArIDIpXSA8PCA2KSB8XG4gICAgICByZXZMb29rdXBbYjY0LmNoYXJDb2RlQXQoaSArIDMpXVxuICAgIGFycltjdXJCeXRlKytdID0gKHRtcCA+PiAxNikgJiAweEZGXG4gICAgYXJyW2N1ckJ5dGUrK10gPSAodG1wID4+IDgpICYgMHhGRlxuICAgIGFycltjdXJCeXRlKytdID0gdG1wICYgMHhGRlxuICB9XG5cbiAgaWYgKHBsYWNlSG9sZGVyc0xlbiA9PT0gMikge1xuICAgIHRtcCA9XG4gICAgICAocmV2TG9va3VwW2I2NC5jaGFyQ29kZUF0KGkpXSA8PCAyKSB8XG4gICAgICAocmV2TG9va3VwW2I2NC5jaGFyQ29kZUF0KGkgKyAxKV0gPj4gNClcbiAgICBhcnJbY3VyQnl0ZSsrXSA9IHRtcCAmIDB4RkZcbiAgfVxuXG4gIGlmIChwbGFjZUhvbGRlcnNMZW4gPT09IDEpIHtcbiAgICB0bXAgPVxuICAgICAgKHJldkxvb2t1cFtiNjQuY2hhckNvZGVBdChpKV0gPDwgMTApIHxcbiAgICAgIChyZXZMb29rdXBbYjY0LmNoYXJDb2RlQXQoaSArIDEpXSA8PCA0KSB8XG4gICAgICAocmV2TG9va3VwW2I2NC5jaGFyQ29kZUF0KGkgKyAyKV0gPj4gMilcbiAgICBhcnJbY3VyQnl0ZSsrXSA9ICh0bXAgPj4gOCkgJiAweEZGXG4gICAgYXJyW2N1ckJ5dGUrK10gPSB0bXAgJiAweEZGXG4gIH1cblxuICByZXR1cm4gYXJyXG59XG5cbmZ1bmN0aW9uIHRyaXBsZXRUb0Jhc2U2NCAobnVtKSB7XG4gIHJldHVybiBsb29rdXBbbnVtID4+IDE4ICYgMHgzRl0gK1xuICAgIGxvb2t1cFtudW0gPj4gMTIgJiAweDNGXSArXG4gICAgbG9va3VwW251bSA+PiA2ICYgMHgzRl0gK1xuICAgIGxvb2t1cFtudW0gJiAweDNGXVxufVxuXG5mdW5jdGlvbiBlbmNvZGVDaHVuayAodWludDgsIHN0YXJ0LCBlbmQpIHtcbiAgdmFyIHRtcFxuICB2YXIgb3V0cHV0ID0gW11cbiAgZm9yICh2YXIgaSA9IHN0YXJ0OyBpIDwgZW5kOyBpICs9IDMpIHtcbiAgICB0bXAgPVxuICAgICAgKCh1aW50OFtpXSA8PCAxNikgJiAweEZGMDAwMCkgK1xuICAgICAgKCh1aW50OFtpICsgMV0gPDwgOCkgJiAweEZGMDApICtcbiAgICAgICh1aW50OFtpICsgMl0gJiAweEZGKVxuICAgIG91dHB1dC5wdXNoKHRyaXBsZXRUb0Jhc2U2NCh0bXApKVxuICB9XG4gIHJldHVybiBvdXRwdXQuam9pbignJylcbn1cblxuZnVuY3Rpb24gZnJvbUJ5dGVBcnJheSAodWludDgpIHtcbiAgdmFyIHRtcFxuICB2YXIgbGVuID0gdWludDgubGVuZ3RoXG4gIHZhciBleHRyYUJ5dGVzID0gbGVuICUgMyAvLyBpZiB3ZSBoYXZlIDEgYnl0ZSBsZWZ0LCBwYWQgMiBieXRlc1xuICB2YXIgcGFydHMgPSBbXVxuICB2YXIgbWF4Q2h1bmtMZW5ndGggPSAxNjM4MyAvLyBtdXN0IGJlIG11bHRpcGxlIG9mIDNcblxuICAvLyBnbyB0aHJvdWdoIHRoZSBhcnJheSBldmVyeSB0aHJlZSBieXRlcywgd2UnbGwgZGVhbCB3aXRoIHRyYWlsaW5nIHN0dWZmIGxhdGVyXG4gIGZvciAodmFyIGkgPSAwLCBsZW4yID0gbGVuIC0gZXh0cmFCeXRlczsgaSA8IGxlbjI7IGkgKz0gbWF4Q2h1bmtMZW5ndGgpIHtcbiAgICBwYXJ0cy5wdXNoKGVuY29kZUNodW5rKFxuICAgICAgdWludDgsIGksIChpICsgbWF4Q2h1bmtMZW5ndGgpID4gbGVuMiA/IGxlbjIgOiAoaSArIG1heENodW5rTGVuZ3RoKVxuICAgICkpXG4gIH1cblxuICAvLyBwYWQgdGhlIGVuZCB3aXRoIHplcm9zLCBidXQgbWFrZSBzdXJlIHRvIG5vdCBmb3JnZXQgdGhlIGV4dHJhIGJ5dGVzXG4gIGlmIChleHRyYUJ5dGVzID09PSAxKSB7XG4gICAgdG1wID0gdWludDhbbGVuIC0gMV1cbiAgICBwYXJ0cy5wdXNoKFxuICAgICAgbG9va3VwW3RtcCA+PiAyXSArXG4gICAgICBsb29rdXBbKHRtcCA8PCA0KSAmIDB4M0ZdICtcbiAgICAgICc9PSdcbiAgICApXG4gIH0gZWxzZSBpZiAoZXh0cmFCeXRlcyA9PT0gMikge1xuICAgIHRtcCA9ICh1aW50OFtsZW4gLSAyXSA8PCA4KSArIHVpbnQ4W2xlbiAtIDFdXG4gICAgcGFydHMucHVzaChcbiAgICAgIGxvb2t1cFt0bXAgPj4gMTBdICtcbiAgICAgIGxvb2t1cFsodG1wID4+IDQpICYgMHgzRl0gK1xuICAgICAgbG9va3VwWyh0bXAgPDwgMikgJiAweDNGXSArXG4gICAgICAnPSdcbiAgICApXG4gIH1cblxuICByZXR1cm4gcGFydHMuam9pbignJylcbn1cbiIsIi8qIVxuICogVGhlIGJ1ZmZlciBtb2R1bGUgZnJvbSBub2RlLmpzLCBmb3IgdGhlIGJyb3dzZXIuXG4gKlxuICogQGF1dGhvciAgIEZlcm9zcyBBYm91a2hhZGlqZWggPGZlcm9zc0BmZXJvc3Mub3JnPiA8aHR0cDovL2Zlcm9zcy5vcmc+XG4gKiBAbGljZW5zZSAgTUlUXG4gKi9cbi8qIGVzbGludC1kaXNhYmxlIG5vLXByb3RvICovXG5cbid1c2Ugc3RyaWN0J1xuXG52YXIgYmFzZTY0ID0gcmVxdWlyZSgnYmFzZTY0LWpzJylcbnZhciBpZWVlNzU0ID0gcmVxdWlyZSgnaWVlZTc1NCcpXG52YXIgaXNBcnJheSA9IHJlcXVpcmUoJ2lzYXJyYXknKVxuXG5leHBvcnRzLkJ1ZmZlciA9IEJ1ZmZlclxuZXhwb3J0cy5TbG93QnVmZmVyID0gU2xvd0J1ZmZlclxuZXhwb3J0cy5JTlNQRUNUX01BWF9CWVRFUyA9IDUwXG5cbi8qKlxuICogSWYgYEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUYDpcbiAqICAgPT09IHRydWUgICAgVXNlIFVpbnQ4QXJyYXkgaW1wbGVtZW50YXRpb24gKGZhc3Rlc3QpXG4gKiAgID09PSBmYWxzZSAgIFVzZSBPYmplY3QgaW1wbGVtZW50YXRpb24gKG1vc3QgY29tcGF0aWJsZSwgZXZlbiBJRTYpXG4gKlxuICogQnJvd3NlcnMgdGhhdCBzdXBwb3J0IHR5cGVkIGFycmF5cyBhcmUgSUUgMTArLCBGaXJlZm94IDQrLCBDaHJvbWUgNyssIFNhZmFyaSA1LjErLFxuICogT3BlcmEgMTEuNissIGlPUyA0LjIrLlxuICpcbiAqIER1ZSB0byB2YXJpb3VzIGJyb3dzZXIgYnVncywgc29tZXRpbWVzIHRoZSBPYmplY3QgaW1wbGVtZW50YXRpb24gd2lsbCBiZSB1c2VkIGV2ZW5cbiAqIHdoZW4gdGhlIGJyb3dzZXIgc3VwcG9ydHMgdHlwZWQgYXJyYXlzLlxuICpcbiAqIE5vdGU6XG4gKlxuICogICAtIEZpcmVmb3ggNC0yOSBsYWNrcyBzdXBwb3J0IGZvciBhZGRpbmcgbmV3IHByb3BlcnRpZXMgdG8gYFVpbnQ4QXJyYXlgIGluc3RhbmNlcyxcbiAqICAgICBTZWU6IGh0dHBzOi8vYnVnemlsbGEubW96aWxsYS5vcmcvc2hvd19idWcuY2dpP2lkPTY5NTQzOC5cbiAqXG4gKiAgIC0gQ2hyb21lIDktMTAgaXMgbWlzc2luZyB0aGUgYFR5cGVkQXJyYXkucHJvdG90eXBlLnN1YmFycmF5YCBmdW5jdGlvbi5cbiAqXG4gKiAgIC0gSUUxMCBoYXMgYSBicm9rZW4gYFR5cGVkQXJyYXkucHJvdG90eXBlLnN1YmFycmF5YCBmdW5jdGlvbiB3aGljaCByZXR1cm5zIGFycmF5cyBvZlxuICogICAgIGluY29ycmVjdCBsZW5ndGggaW4gc29tZSBzaXR1YXRpb25zLlxuXG4gKiBXZSBkZXRlY3QgdGhlc2UgYnVnZ3kgYnJvd3NlcnMgYW5kIHNldCBgQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlRgIHRvIGBmYWxzZWAgc28gdGhleVxuICogZ2V0IHRoZSBPYmplY3QgaW1wbGVtZW50YXRpb24sIHdoaWNoIGlzIHNsb3dlciBidXQgYmVoYXZlcyBjb3JyZWN0bHkuXG4gKi9cbkJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUID0gZ2xvYmFsLlRZUEVEX0FSUkFZX1NVUFBPUlQgIT09IHVuZGVmaW5lZFxuICA/IGdsb2JhbC5UWVBFRF9BUlJBWV9TVVBQT1JUXG4gIDogdHlwZWRBcnJheVN1cHBvcnQoKVxuXG4vKlxuICogRXhwb3J0IGtNYXhMZW5ndGggYWZ0ZXIgdHlwZWQgYXJyYXkgc3VwcG9ydCBpcyBkZXRlcm1pbmVkLlxuICovXG5leHBvcnRzLmtNYXhMZW5ndGggPSBrTWF4TGVuZ3RoKClcblxuZnVuY3Rpb24gdHlwZWRBcnJheVN1cHBvcnQgKCkge1xuICB0cnkge1xuICAgIHZhciBhcnIgPSBuZXcgVWludDhBcnJheSgxKVxuICAgIGFyci5fX3Byb3RvX18gPSB7X19wcm90b19fOiBVaW50OEFycmF5LnByb3RvdHlwZSwgZm9vOiBmdW5jdGlvbiAoKSB7IHJldHVybiA0MiB9fVxuICAgIHJldHVybiBhcnIuZm9vKCkgPT09IDQyICYmIC8vIHR5cGVkIGFycmF5IGluc3RhbmNlcyBjYW4gYmUgYXVnbWVudGVkXG4gICAgICAgIHR5cGVvZiBhcnIuc3ViYXJyYXkgPT09ICdmdW5jdGlvbicgJiYgLy8gY2hyb21lIDktMTAgbGFjayBgc3ViYXJyYXlgXG4gICAgICAgIGFyci5zdWJhcnJheSgxLCAxKS5ieXRlTGVuZ3RoID09PSAwIC8vIGllMTAgaGFzIGJyb2tlbiBgc3ViYXJyYXlgXG4gIH0gY2F0Y2ggKGUpIHtcbiAgICByZXR1cm4gZmFsc2VcbiAgfVxufVxuXG5mdW5jdGlvbiBrTWF4TGVuZ3RoICgpIHtcbiAgcmV0dXJuIEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUXG4gICAgPyAweDdmZmZmZmZmXG4gICAgOiAweDNmZmZmZmZmXG59XG5cbmZ1bmN0aW9uIGNyZWF0ZUJ1ZmZlciAodGhhdCwgbGVuZ3RoKSB7XG4gIGlmIChrTWF4TGVuZ3RoKCkgPCBsZW5ndGgpIHtcbiAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcignSW52YWxpZCB0eXBlZCBhcnJheSBsZW5ndGgnKVxuICB9XG4gIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIC8vIFJldHVybiBhbiBhdWdtZW50ZWQgYFVpbnQ4QXJyYXlgIGluc3RhbmNlLCBmb3IgYmVzdCBwZXJmb3JtYW5jZVxuICAgIHRoYXQgPSBuZXcgVWludDhBcnJheShsZW5ndGgpXG4gICAgdGhhdC5fX3Byb3RvX18gPSBCdWZmZXIucHJvdG90eXBlXG4gIH0gZWxzZSB7XG4gICAgLy8gRmFsbGJhY2s6IFJldHVybiBhbiBvYmplY3QgaW5zdGFuY2Ugb2YgdGhlIEJ1ZmZlciBjbGFzc1xuICAgIGlmICh0aGF0ID09PSBudWxsKSB7XG4gICAgICB0aGF0ID0gbmV3IEJ1ZmZlcihsZW5ndGgpXG4gICAgfVxuICAgIHRoYXQubGVuZ3RoID0gbGVuZ3RoXG4gIH1cblxuICByZXR1cm4gdGhhdFxufVxuXG4vKipcbiAqIFRoZSBCdWZmZXIgY29uc3RydWN0b3IgcmV0dXJucyBpbnN0YW5jZXMgb2YgYFVpbnQ4QXJyYXlgIHRoYXQgaGF2ZSB0aGVpclxuICogcHJvdG90eXBlIGNoYW5nZWQgdG8gYEJ1ZmZlci5wcm90b3R5cGVgLiBGdXJ0aGVybW9yZSwgYEJ1ZmZlcmAgaXMgYSBzdWJjbGFzcyBvZlxuICogYFVpbnQ4QXJyYXlgLCBzbyB0aGUgcmV0dXJuZWQgaW5zdGFuY2VzIHdpbGwgaGF2ZSBhbGwgdGhlIG5vZGUgYEJ1ZmZlcmAgbWV0aG9kc1xuICogYW5kIHRoZSBgVWludDhBcnJheWAgbWV0aG9kcy4gU3F1YXJlIGJyYWNrZXQgbm90YXRpb24gd29ya3MgYXMgZXhwZWN0ZWQgLS0gaXRcbiAqIHJldHVybnMgYSBzaW5nbGUgb2N0ZXQuXG4gKlxuICogVGhlIGBVaW50OEFycmF5YCBwcm90b3R5cGUgcmVtYWlucyB1bm1vZGlmaWVkLlxuICovXG5cbmZ1bmN0aW9uIEJ1ZmZlciAoYXJnLCBlbmNvZGluZ09yT2Zmc2V0LCBsZW5ndGgpIHtcbiAgaWYgKCFCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCAmJiAhKHRoaXMgaW5zdGFuY2VvZiBCdWZmZXIpKSB7XG4gICAgcmV0dXJuIG5ldyBCdWZmZXIoYXJnLCBlbmNvZGluZ09yT2Zmc2V0LCBsZW5ndGgpXG4gIH1cblxuICAvLyBDb21tb24gY2FzZS5cbiAgaWYgKHR5cGVvZiBhcmcgPT09ICdudW1iZXInKSB7XG4gICAgaWYgKHR5cGVvZiBlbmNvZGluZ09yT2Zmc2V0ID09PSAnc3RyaW5nJykge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKFxuICAgICAgICAnSWYgZW5jb2RpbmcgaXMgc3BlY2lmaWVkIHRoZW4gdGhlIGZpcnN0IGFyZ3VtZW50IG11c3QgYmUgYSBzdHJpbmcnXG4gICAgICApXG4gICAgfVxuICAgIHJldHVybiBhbGxvY1Vuc2FmZSh0aGlzLCBhcmcpXG4gIH1cbiAgcmV0dXJuIGZyb20odGhpcywgYXJnLCBlbmNvZGluZ09yT2Zmc2V0LCBsZW5ndGgpXG59XG5cbkJ1ZmZlci5wb29sU2l6ZSA9IDgxOTIgLy8gbm90IHVzZWQgYnkgdGhpcyBpbXBsZW1lbnRhdGlvblxuXG4vLyBUT0RPOiBMZWdhY3ksIG5vdCBuZWVkZWQgYW55bW9yZS4gUmVtb3ZlIGluIG5leHQgbWFqb3IgdmVyc2lvbi5cbkJ1ZmZlci5fYXVnbWVudCA9IGZ1bmN0aW9uIChhcnIpIHtcbiAgYXJyLl9fcHJvdG9fXyA9IEJ1ZmZlci5wcm90b3R5cGVcbiAgcmV0dXJuIGFyclxufVxuXG5mdW5jdGlvbiBmcm9tICh0aGF0LCB2YWx1ZSwgZW5jb2RpbmdPck9mZnNldCwgbGVuZ3RoKSB7XG4gIGlmICh0eXBlb2YgdmFsdWUgPT09ICdudW1iZXInKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcignXCJ2YWx1ZVwiIGFyZ3VtZW50IG11c3Qgbm90IGJlIGEgbnVtYmVyJylcbiAgfVxuXG4gIGlmICh0eXBlb2YgQXJyYXlCdWZmZXIgIT09ICd1bmRlZmluZWQnICYmIHZhbHVlIGluc3RhbmNlb2YgQXJyYXlCdWZmZXIpIHtcbiAgICByZXR1cm4gZnJvbUFycmF5QnVmZmVyKHRoYXQsIHZhbHVlLCBlbmNvZGluZ09yT2Zmc2V0LCBsZW5ndGgpXG4gIH1cblxuICBpZiAodHlwZW9mIHZhbHVlID09PSAnc3RyaW5nJykge1xuICAgIHJldHVybiBmcm9tU3RyaW5nKHRoYXQsIHZhbHVlLCBlbmNvZGluZ09yT2Zmc2V0KVxuICB9XG5cbiAgcmV0dXJuIGZyb21PYmplY3QodGhhdCwgdmFsdWUpXG59XG5cbi8qKlxuICogRnVuY3Rpb25hbGx5IGVxdWl2YWxlbnQgdG8gQnVmZmVyKGFyZywgZW5jb2RpbmcpIGJ1dCB0aHJvd3MgYSBUeXBlRXJyb3JcbiAqIGlmIHZhbHVlIGlzIGEgbnVtYmVyLlxuICogQnVmZmVyLmZyb20oc3RyWywgZW5jb2RpbmddKVxuICogQnVmZmVyLmZyb20oYXJyYXkpXG4gKiBCdWZmZXIuZnJvbShidWZmZXIpXG4gKiBCdWZmZXIuZnJvbShhcnJheUJ1ZmZlclssIGJ5dGVPZmZzZXRbLCBsZW5ndGhdXSlcbiAqKi9cbkJ1ZmZlci5mcm9tID0gZnVuY3Rpb24gKHZhbHVlLCBlbmNvZGluZ09yT2Zmc2V0LCBsZW5ndGgpIHtcbiAgcmV0dXJuIGZyb20obnVsbCwgdmFsdWUsIGVuY29kaW5nT3JPZmZzZXQsIGxlbmd0aClcbn1cblxuaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gIEJ1ZmZlci5wcm90b3R5cGUuX19wcm90b19fID0gVWludDhBcnJheS5wcm90b3R5cGVcbiAgQnVmZmVyLl9fcHJvdG9fXyA9IFVpbnQ4QXJyYXlcbiAgaWYgKHR5cGVvZiBTeW1ib2wgIT09ICd1bmRlZmluZWQnICYmIFN5bWJvbC5zcGVjaWVzICYmXG4gICAgICBCdWZmZXJbU3ltYm9sLnNwZWNpZXNdID09PSBCdWZmZXIpIHtcbiAgICAvLyBGaXggc3ViYXJyYXkoKSBpbiBFUzIwMTYuIFNlZTogaHR0cHM6Ly9naXRodWIuY29tL2Zlcm9zcy9idWZmZXIvcHVsbC85N1xuICAgIE9iamVjdC5kZWZpbmVQcm9wZXJ0eShCdWZmZXIsIFN5bWJvbC5zcGVjaWVzLCB7XG4gICAgICB2YWx1ZTogbnVsbCxcbiAgICAgIGNvbmZpZ3VyYWJsZTogdHJ1ZVxuICAgIH0pXG4gIH1cbn1cblxuZnVuY3Rpb24gYXNzZXJ0U2l6ZSAoc2l6ZSkge1xuICBpZiAodHlwZW9mIHNpemUgIT09ICdudW1iZXInKSB7XG4gICAgdGhyb3cgbmV3IFR5cGVFcnJvcignXCJzaXplXCIgYXJndW1lbnQgbXVzdCBiZSBhIG51bWJlcicpXG4gIH0gZWxzZSBpZiAoc2l6ZSA8IDApIHtcbiAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcignXCJzaXplXCIgYXJndW1lbnQgbXVzdCBub3QgYmUgbmVnYXRpdmUnKVxuICB9XG59XG5cbmZ1bmN0aW9uIGFsbG9jICh0aGF0LCBzaXplLCBmaWxsLCBlbmNvZGluZykge1xuICBhc3NlcnRTaXplKHNpemUpXG4gIGlmIChzaXplIDw9IDApIHtcbiAgICByZXR1cm4gY3JlYXRlQnVmZmVyKHRoYXQsIHNpemUpXG4gIH1cbiAgaWYgKGZpbGwgIT09IHVuZGVmaW5lZCkge1xuICAgIC8vIE9ubHkgcGF5IGF0dGVudGlvbiB0byBlbmNvZGluZyBpZiBpdCdzIGEgc3RyaW5nLiBUaGlzXG4gICAgLy8gcHJldmVudHMgYWNjaWRlbnRhbGx5IHNlbmRpbmcgaW4gYSBudW1iZXIgdGhhdCB3b3VsZFxuICAgIC8vIGJlIGludGVycHJldHRlZCBhcyBhIHN0YXJ0IG9mZnNldC5cbiAgICByZXR1cm4gdHlwZW9mIGVuY29kaW5nID09PSAnc3RyaW5nJ1xuICAgICAgPyBjcmVhdGVCdWZmZXIodGhhdCwgc2l6ZSkuZmlsbChmaWxsLCBlbmNvZGluZylcbiAgICAgIDogY3JlYXRlQnVmZmVyKHRoYXQsIHNpemUpLmZpbGwoZmlsbClcbiAgfVxuICByZXR1cm4gY3JlYXRlQnVmZmVyKHRoYXQsIHNpemUpXG59XG5cbi8qKlxuICogQ3JlYXRlcyBhIG5ldyBmaWxsZWQgQnVmZmVyIGluc3RhbmNlLlxuICogYWxsb2Moc2l6ZVssIGZpbGxbLCBlbmNvZGluZ11dKVxuICoqL1xuQnVmZmVyLmFsbG9jID0gZnVuY3Rpb24gKHNpemUsIGZpbGwsIGVuY29kaW5nKSB7XG4gIHJldHVybiBhbGxvYyhudWxsLCBzaXplLCBmaWxsLCBlbmNvZGluZylcbn1cblxuZnVuY3Rpb24gYWxsb2NVbnNhZmUgKHRoYXQsIHNpemUpIHtcbiAgYXNzZXJ0U2l6ZShzaXplKVxuICB0aGF0ID0gY3JlYXRlQnVmZmVyKHRoYXQsIHNpemUgPCAwID8gMCA6IGNoZWNrZWQoc2l6ZSkgfCAwKVxuICBpZiAoIUJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzaXplOyArK2kpIHtcbiAgICAgIHRoYXRbaV0gPSAwXG4gICAgfVxuICB9XG4gIHJldHVybiB0aGF0XG59XG5cbi8qKlxuICogRXF1aXZhbGVudCB0byBCdWZmZXIobnVtKSwgYnkgZGVmYXVsdCBjcmVhdGVzIGEgbm9uLXplcm8tZmlsbGVkIEJ1ZmZlciBpbnN0YW5jZS5cbiAqICovXG5CdWZmZXIuYWxsb2NVbnNhZmUgPSBmdW5jdGlvbiAoc2l6ZSkge1xuICByZXR1cm4gYWxsb2NVbnNhZmUobnVsbCwgc2l6ZSlcbn1cbi8qKlxuICogRXF1aXZhbGVudCB0byBTbG93QnVmZmVyKG51bSksIGJ5IGRlZmF1bHQgY3JlYXRlcyBhIG5vbi16ZXJvLWZpbGxlZCBCdWZmZXIgaW5zdGFuY2UuXG4gKi9cbkJ1ZmZlci5hbGxvY1Vuc2FmZVNsb3cgPSBmdW5jdGlvbiAoc2l6ZSkge1xuICByZXR1cm4gYWxsb2NVbnNhZmUobnVsbCwgc2l6ZSlcbn1cblxuZnVuY3Rpb24gZnJvbVN0cmluZyAodGhhdCwgc3RyaW5nLCBlbmNvZGluZykge1xuICBpZiAodHlwZW9mIGVuY29kaW5nICE9PSAnc3RyaW5nJyB8fCBlbmNvZGluZyA9PT0gJycpIHtcbiAgICBlbmNvZGluZyA9ICd1dGY4J1xuICB9XG5cbiAgaWYgKCFCdWZmZXIuaXNFbmNvZGluZyhlbmNvZGluZykpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdcImVuY29kaW5nXCIgbXVzdCBiZSBhIHZhbGlkIHN0cmluZyBlbmNvZGluZycpXG4gIH1cblxuICB2YXIgbGVuZ3RoID0gYnl0ZUxlbmd0aChzdHJpbmcsIGVuY29kaW5nKSB8IDBcbiAgdGhhdCA9IGNyZWF0ZUJ1ZmZlcih0aGF0LCBsZW5ndGgpXG5cbiAgdmFyIGFjdHVhbCA9IHRoYXQud3JpdGUoc3RyaW5nLCBlbmNvZGluZylcblxuICBpZiAoYWN0dWFsICE9PSBsZW5ndGgpIHtcbiAgICAvLyBXcml0aW5nIGEgaGV4IHN0cmluZywgZm9yIGV4YW1wbGUsIHRoYXQgY29udGFpbnMgaW52YWxpZCBjaGFyYWN0ZXJzIHdpbGxcbiAgICAvLyBjYXVzZSBldmVyeXRoaW5nIGFmdGVyIHRoZSBmaXJzdCBpbnZhbGlkIGNoYXJhY3RlciB0byBiZSBpZ25vcmVkLiAoZS5nLlxuICAgIC8vICdhYnh4Y2QnIHdpbGwgYmUgdHJlYXRlZCBhcyAnYWInKVxuICAgIHRoYXQgPSB0aGF0LnNsaWNlKDAsIGFjdHVhbClcbiAgfVxuXG4gIHJldHVybiB0aGF0XG59XG5cbmZ1bmN0aW9uIGZyb21BcnJheUxpa2UgKHRoYXQsIGFycmF5KSB7XG4gIHZhciBsZW5ndGggPSBhcnJheS5sZW5ndGggPCAwID8gMCA6IGNoZWNrZWQoYXJyYXkubGVuZ3RoKSB8IDBcbiAgdGhhdCA9IGNyZWF0ZUJ1ZmZlcih0aGF0LCBsZW5ndGgpXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGVuZ3RoOyBpICs9IDEpIHtcbiAgICB0aGF0W2ldID0gYXJyYXlbaV0gJiAyNTVcbiAgfVxuICByZXR1cm4gdGhhdFxufVxuXG5mdW5jdGlvbiBmcm9tQXJyYXlCdWZmZXIgKHRoYXQsIGFycmF5LCBieXRlT2Zmc2V0LCBsZW5ndGgpIHtcbiAgYXJyYXkuYnl0ZUxlbmd0aCAvLyB0aGlzIHRocm93cyBpZiBgYXJyYXlgIGlzIG5vdCBhIHZhbGlkIEFycmF5QnVmZmVyXG5cbiAgaWYgKGJ5dGVPZmZzZXQgPCAwIHx8IGFycmF5LmJ5dGVMZW5ndGggPCBieXRlT2Zmc2V0KSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ1xcJ29mZnNldFxcJyBpcyBvdXQgb2YgYm91bmRzJylcbiAgfVxuXG4gIGlmIChhcnJheS5ieXRlTGVuZ3RoIDwgYnl0ZU9mZnNldCArIChsZW5ndGggfHwgMCkpIHtcbiAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcignXFwnbGVuZ3RoXFwnIGlzIG91dCBvZiBib3VuZHMnKVxuICB9XG5cbiAgaWYgKGJ5dGVPZmZzZXQgPT09IHVuZGVmaW5lZCAmJiBsZW5ndGggPT09IHVuZGVmaW5lZCkge1xuICAgIGFycmF5ID0gbmV3IFVpbnQ4QXJyYXkoYXJyYXkpXG4gIH0gZWxzZSBpZiAobGVuZ3RoID09PSB1bmRlZmluZWQpIHtcbiAgICBhcnJheSA9IG5ldyBVaW50OEFycmF5KGFycmF5LCBieXRlT2Zmc2V0KVxuICB9IGVsc2Uge1xuICAgIGFycmF5ID0gbmV3IFVpbnQ4QXJyYXkoYXJyYXksIGJ5dGVPZmZzZXQsIGxlbmd0aClcbiAgfVxuXG4gIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIC8vIFJldHVybiBhbiBhdWdtZW50ZWQgYFVpbnQ4QXJyYXlgIGluc3RhbmNlLCBmb3IgYmVzdCBwZXJmb3JtYW5jZVxuICAgIHRoYXQgPSBhcnJheVxuICAgIHRoYXQuX19wcm90b19fID0gQnVmZmVyLnByb3RvdHlwZVxuICB9IGVsc2Uge1xuICAgIC8vIEZhbGxiYWNrOiBSZXR1cm4gYW4gb2JqZWN0IGluc3RhbmNlIG9mIHRoZSBCdWZmZXIgY2xhc3NcbiAgICB0aGF0ID0gZnJvbUFycmF5TGlrZSh0aGF0LCBhcnJheSlcbiAgfVxuICByZXR1cm4gdGhhdFxufVxuXG5mdW5jdGlvbiBmcm9tT2JqZWN0ICh0aGF0LCBvYmopIHtcbiAgaWYgKEJ1ZmZlci5pc0J1ZmZlcihvYmopKSB7XG4gICAgdmFyIGxlbiA9IGNoZWNrZWQob2JqLmxlbmd0aCkgfCAwXG4gICAgdGhhdCA9IGNyZWF0ZUJ1ZmZlcih0aGF0LCBsZW4pXG5cbiAgICBpZiAodGhhdC5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiB0aGF0XG4gICAgfVxuXG4gICAgb2JqLmNvcHkodGhhdCwgMCwgMCwgbGVuKVxuICAgIHJldHVybiB0aGF0XG4gIH1cblxuICBpZiAob2JqKSB7XG4gICAgaWYgKCh0eXBlb2YgQXJyYXlCdWZmZXIgIT09ICd1bmRlZmluZWQnICYmXG4gICAgICAgIG9iai5idWZmZXIgaW5zdGFuY2VvZiBBcnJheUJ1ZmZlcikgfHwgJ2xlbmd0aCcgaW4gb2JqKSB7XG4gICAgICBpZiAodHlwZW9mIG9iai5sZW5ndGggIT09ICdudW1iZXInIHx8IGlzbmFuKG9iai5sZW5ndGgpKSB7XG4gICAgICAgIHJldHVybiBjcmVhdGVCdWZmZXIodGhhdCwgMClcbiAgICAgIH1cbiAgICAgIHJldHVybiBmcm9tQXJyYXlMaWtlKHRoYXQsIG9iailcbiAgICB9XG5cbiAgICBpZiAob2JqLnR5cGUgPT09ICdCdWZmZXInICYmIGlzQXJyYXkob2JqLmRhdGEpKSB7XG4gICAgICByZXR1cm4gZnJvbUFycmF5TGlrZSh0aGF0LCBvYmouZGF0YSlcbiAgICB9XG4gIH1cblxuICB0aHJvdyBuZXcgVHlwZUVycm9yKCdGaXJzdCBhcmd1bWVudCBtdXN0IGJlIGEgc3RyaW5nLCBCdWZmZXIsIEFycmF5QnVmZmVyLCBBcnJheSwgb3IgYXJyYXktbGlrZSBvYmplY3QuJylcbn1cblxuZnVuY3Rpb24gY2hlY2tlZCAobGVuZ3RoKSB7XG4gIC8vIE5vdGU6IGNhbm5vdCB1c2UgYGxlbmd0aCA8IGtNYXhMZW5ndGgoKWAgaGVyZSBiZWNhdXNlIHRoYXQgZmFpbHMgd2hlblxuICAvLyBsZW5ndGggaXMgTmFOICh3aGljaCBpcyBvdGhlcndpc2UgY29lcmNlZCB0byB6ZXJvLilcbiAgaWYgKGxlbmd0aCA+PSBrTWF4TGVuZ3RoKCkpIHtcbiAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcignQXR0ZW1wdCB0byBhbGxvY2F0ZSBCdWZmZXIgbGFyZ2VyIHRoYW4gbWF4aW11bSAnICtcbiAgICAgICAgICAgICAgICAgICAgICAgICAnc2l6ZTogMHgnICsga01heExlbmd0aCgpLnRvU3RyaW5nKDE2KSArICcgYnl0ZXMnKVxuICB9XG4gIHJldHVybiBsZW5ndGggfCAwXG59XG5cbmZ1bmN0aW9uIFNsb3dCdWZmZXIgKGxlbmd0aCkge1xuICBpZiAoK2xlbmd0aCAhPSBsZW5ndGgpIHsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBlcWVxZXFcbiAgICBsZW5ndGggPSAwXG4gIH1cbiAgcmV0dXJuIEJ1ZmZlci5hbGxvYygrbGVuZ3RoKVxufVxuXG5CdWZmZXIuaXNCdWZmZXIgPSBmdW5jdGlvbiBpc0J1ZmZlciAoYikge1xuICByZXR1cm4gISEoYiAhPSBudWxsICYmIGIuX2lzQnVmZmVyKVxufVxuXG5CdWZmZXIuY29tcGFyZSA9IGZ1bmN0aW9uIGNvbXBhcmUgKGEsIGIpIHtcbiAgaWYgKCFCdWZmZXIuaXNCdWZmZXIoYSkgfHwgIUJ1ZmZlci5pc0J1ZmZlcihiKSkge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ0FyZ3VtZW50cyBtdXN0IGJlIEJ1ZmZlcnMnKVxuICB9XG5cbiAgaWYgKGEgPT09IGIpIHJldHVybiAwXG5cbiAgdmFyIHggPSBhLmxlbmd0aFxuICB2YXIgeSA9IGIubGVuZ3RoXG5cbiAgZm9yICh2YXIgaSA9IDAsIGxlbiA9IE1hdGgubWluKHgsIHkpOyBpIDwgbGVuOyArK2kpIHtcbiAgICBpZiAoYVtpXSAhPT0gYltpXSkge1xuICAgICAgeCA9IGFbaV1cbiAgICAgIHkgPSBiW2ldXG4gICAgICBicmVha1xuICAgIH1cbiAgfVxuXG4gIGlmICh4IDwgeSkgcmV0dXJuIC0xXG4gIGlmICh5IDwgeCkgcmV0dXJuIDFcbiAgcmV0dXJuIDBcbn1cblxuQnVmZmVyLmlzRW5jb2RpbmcgPSBmdW5jdGlvbiBpc0VuY29kaW5nIChlbmNvZGluZykge1xuICBzd2l0Y2ggKFN0cmluZyhlbmNvZGluZykudG9Mb3dlckNhc2UoKSkge1xuICAgIGNhc2UgJ2hleCc6XG4gICAgY2FzZSAndXRmOCc6XG4gICAgY2FzZSAndXRmLTgnOlxuICAgIGNhc2UgJ2FzY2lpJzpcbiAgICBjYXNlICdsYXRpbjEnOlxuICAgIGNhc2UgJ2JpbmFyeSc6XG4gICAgY2FzZSAnYmFzZTY0JzpcbiAgICBjYXNlICd1Y3MyJzpcbiAgICBjYXNlICd1Y3MtMic6XG4gICAgY2FzZSAndXRmMTZsZSc6XG4gICAgY2FzZSAndXRmLTE2bGUnOlxuICAgICAgcmV0dXJuIHRydWVcbiAgICBkZWZhdWx0OlxuICAgICAgcmV0dXJuIGZhbHNlXG4gIH1cbn1cblxuQnVmZmVyLmNvbmNhdCA9IGZ1bmN0aW9uIGNvbmNhdCAobGlzdCwgbGVuZ3RoKSB7XG4gIGlmICghaXNBcnJheShsaXN0KSkge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1wibGlzdFwiIGFyZ3VtZW50IG11c3QgYmUgYW4gQXJyYXkgb2YgQnVmZmVycycpXG4gIH1cblxuICBpZiAobGlzdC5sZW5ndGggPT09IDApIHtcbiAgICByZXR1cm4gQnVmZmVyLmFsbG9jKDApXG4gIH1cblxuICB2YXIgaVxuICBpZiAobGVuZ3RoID09PSB1bmRlZmluZWQpIHtcbiAgICBsZW5ndGggPSAwXG4gICAgZm9yIChpID0gMDsgaSA8IGxpc3QubGVuZ3RoOyArK2kpIHtcbiAgICAgIGxlbmd0aCArPSBsaXN0W2ldLmxlbmd0aFxuICAgIH1cbiAgfVxuXG4gIHZhciBidWZmZXIgPSBCdWZmZXIuYWxsb2NVbnNhZmUobGVuZ3RoKVxuICB2YXIgcG9zID0gMFxuICBmb3IgKGkgPSAwOyBpIDwgbGlzdC5sZW5ndGg7ICsraSkge1xuICAgIHZhciBidWYgPSBsaXN0W2ldXG4gICAgaWYgKCFCdWZmZXIuaXNCdWZmZXIoYnVmKSkge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignXCJsaXN0XCIgYXJndW1lbnQgbXVzdCBiZSBhbiBBcnJheSBvZiBCdWZmZXJzJylcbiAgICB9XG4gICAgYnVmLmNvcHkoYnVmZmVyLCBwb3MpXG4gICAgcG9zICs9IGJ1Zi5sZW5ndGhcbiAgfVxuICByZXR1cm4gYnVmZmVyXG59XG5cbmZ1bmN0aW9uIGJ5dGVMZW5ndGggKHN0cmluZywgZW5jb2RpbmcpIHtcbiAgaWYgKEJ1ZmZlci5pc0J1ZmZlcihzdHJpbmcpKSB7XG4gICAgcmV0dXJuIHN0cmluZy5sZW5ndGhcbiAgfVxuICBpZiAodHlwZW9mIEFycmF5QnVmZmVyICE9PSAndW5kZWZpbmVkJyAmJiB0eXBlb2YgQXJyYXlCdWZmZXIuaXNWaWV3ID09PSAnZnVuY3Rpb24nICYmXG4gICAgICAoQXJyYXlCdWZmZXIuaXNWaWV3KHN0cmluZykgfHwgc3RyaW5nIGluc3RhbmNlb2YgQXJyYXlCdWZmZXIpKSB7XG4gICAgcmV0dXJuIHN0cmluZy5ieXRlTGVuZ3RoXG4gIH1cbiAgaWYgKHR5cGVvZiBzdHJpbmcgIT09ICdzdHJpbmcnKSB7XG4gICAgc3RyaW5nID0gJycgKyBzdHJpbmdcbiAgfVxuXG4gIHZhciBsZW4gPSBzdHJpbmcubGVuZ3RoXG4gIGlmIChsZW4gPT09IDApIHJldHVybiAwXG5cbiAgLy8gVXNlIGEgZm9yIGxvb3AgdG8gYXZvaWQgcmVjdXJzaW9uXG4gIHZhciBsb3dlcmVkQ2FzZSA9IGZhbHNlXG4gIGZvciAoOzspIHtcbiAgICBzd2l0Y2ggKGVuY29kaW5nKSB7XG4gICAgICBjYXNlICdhc2NpaSc6XG4gICAgICBjYXNlICdsYXRpbjEnOlxuICAgICAgY2FzZSAnYmluYXJ5JzpcbiAgICAgICAgcmV0dXJuIGxlblxuICAgICAgY2FzZSAndXRmOCc6XG4gICAgICBjYXNlICd1dGYtOCc6XG4gICAgICBjYXNlIHVuZGVmaW5lZDpcbiAgICAgICAgcmV0dXJuIHV0ZjhUb0J5dGVzKHN0cmluZykubGVuZ3RoXG4gICAgICBjYXNlICd1Y3MyJzpcbiAgICAgIGNhc2UgJ3Vjcy0yJzpcbiAgICAgIGNhc2UgJ3V0ZjE2bGUnOlxuICAgICAgY2FzZSAndXRmLTE2bGUnOlxuICAgICAgICByZXR1cm4gbGVuICogMlxuICAgICAgY2FzZSAnaGV4JzpcbiAgICAgICAgcmV0dXJuIGxlbiA+Pj4gMVxuICAgICAgY2FzZSAnYmFzZTY0JzpcbiAgICAgICAgcmV0dXJuIGJhc2U2NFRvQnl0ZXMoc3RyaW5nKS5sZW5ndGhcbiAgICAgIGRlZmF1bHQ6XG4gICAgICAgIGlmIChsb3dlcmVkQ2FzZSkgcmV0dXJuIHV0ZjhUb0J5dGVzKHN0cmluZykubGVuZ3RoIC8vIGFzc3VtZSB1dGY4XG4gICAgICAgIGVuY29kaW5nID0gKCcnICsgZW5jb2RpbmcpLnRvTG93ZXJDYXNlKClcbiAgICAgICAgbG93ZXJlZENhc2UgPSB0cnVlXG4gICAgfVxuICB9XG59XG5CdWZmZXIuYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGhcblxuZnVuY3Rpb24gc2xvd1RvU3RyaW5nIChlbmNvZGluZywgc3RhcnQsIGVuZCkge1xuICB2YXIgbG93ZXJlZENhc2UgPSBmYWxzZVxuXG4gIC8vIE5vIG5lZWQgdG8gdmVyaWZ5IHRoYXQgXCJ0aGlzLmxlbmd0aCA8PSBNQVhfVUlOVDMyXCIgc2luY2UgaXQncyBhIHJlYWQtb25seVxuICAvLyBwcm9wZXJ0eSBvZiBhIHR5cGVkIGFycmF5LlxuXG4gIC8vIFRoaXMgYmVoYXZlcyBuZWl0aGVyIGxpa2UgU3RyaW5nIG5vciBVaW50OEFycmF5IGluIHRoYXQgd2Ugc2V0IHN0YXJ0L2VuZFxuICAvLyB0byB0aGVpciB1cHBlci9sb3dlciBib3VuZHMgaWYgdGhlIHZhbHVlIHBhc3NlZCBpcyBvdXQgb2YgcmFuZ2UuXG4gIC8vIHVuZGVmaW5lZCBpcyBoYW5kbGVkIHNwZWNpYWxseSBhcyBwZXIgRUNNQS0yNjIgNnRoIEVkaXRpb24sXG4gIC8vIFNlY3Rpb24gMTMuMy4zLjcgUnVudGltZSBTZW1hbnRpY3M6IEtleWVkQmluZGluZ0luaXRpYWxpemF0aW9uLlxuICBpZiAoc3RhcnQgPT09IHVuZGVmaW5lZCB8fCBzdGFydCA8IDApIHtcbiAgICBzdGFydCA9IDBcbiAgfVxuICAvLyBSZXR1cm4gZWFybHkgaWYgc3RhcnQgPiB0aGlzLmxlbmd0aC4gRG9uZSBoZXJlIHRvIHByZXZlbnQgcG90ZW50aWFsIHVpbnQzMlxuICAvLyBjb2VyY2lvbiBmYWlsIGJlbG93LlxuICBpZiAoc3RhcnQgPiB0aGlzLmxlbmd0aCkge1xuICAgIHJldHVybiAnJ1xuICB9XG5cbiAgaWYgKGVuZCA9PT0gdW5kZWZpbmVkIHx8IGVuZCA+IHRoaXMubGVuZ3RoKSB7XG4gICAgZW5kID0gdGhpcy5sZW5ndGhcbiAgfVxuXG4gIGlmIChlbmQgPD0gMCkge1xuICAgIHJldHVybiAnJ1xuICB9XG5cbiAgLy8gRm9yY2UgY29lcnNpb24gdG8gdWludDMyLiBUaGlzIHdpbGwgYWxzbyBjb2VyY2UgZmFsc2V5L05hTiB2YWx1ZXMgdG8gMC5cbiAgZW5kID4+Pj0gMFxuICBzdGFydCA+Pj49IDBcblxuICBpZiAoZW5kIDw9IHN0YXJ0KSB7XG4gICAgcmV0dXJuICcnXG4gIH1cblxuICBpZiAoIWVuY29kaW5nKSBlbmNvZGluZyA9ICd1dGY4J1xuXG4gIHdoaWxlICh0cnVlKSB7XG4gICAgc3dpdGNoIChlbmNvZGluZykge1xuICAgICAgY2FzZSAnaGV4JzpcbiAgICAgICAgcmV0dXJuIGhleFNsaWNlKHRoaXMsIHN0YXJ0LCBlbmQpXG5cbiAgICAgIGNhc2UgJ3V0ZjgnOlxuICAgICAgY2FzZSAndXRmLTgnOlxuICAgICAgICByZXR1cm4gdXRmOFNsaWNlKHRoaXMsIHN0YXJ0LCBlbmQpXG5cbiAgICAgIGNhc2UgJ2FzY2lpJzpcbiAgICAgICAgcmV0dXJuIGFzY2lpU2xpY2UodGhpcywgc3RhcnQsIGVuZClcblxuICAgICAgY2FzZSAnbGF0aW4xJzpcbiAgICAgIGNhc2UgJ2JpbmFyeSc6XG4gICAgICAgIHJldHVybiBsYXRpbjFTbGljZSh0aGlzLCBzdGFydCwgZW5kKVxuXG4gICAgICBjYXNlICdiYXNlNjQnOlxuICAgICAgICByZXR1cm4gYmFzZTY0U2xpY2UodGhpcywgc3RhcnQsIGVuZClcblxuICAgICAgY2FzZSAndWNzMic6XG4gICAgICBjYXNlICd1Y3MtMic6XG4gICAgICBjYXNlICd1dGYxNmxlJzpcbiAgICAgIGNhc2UgJ3V0Zi0xNmxlJzpcbiAgICAgICAgcmV0dXJuIHV0ZjE2bGVTbGljZSh0aGlzLCBzdGFydCwgZW5kKVxuXG4gICAgICBkZWZhdWx0OlxuICAgICAgICBpZiAobG93ZXJlZENhc2UpIHRocm93IG5ldyBUeXBlRXJyb3IoJ1Vua25vd24gZW5jb2Rpbmc6ICcgKyBlbmNvZGluZylcbiAgICAgICAgZW5jb2RpbmcgPSAoZW5jb2RpbmcgKyAnJykudG9Mb3dlckNhc2UoKVxuICAgICAgICBsb3dlcmVkQ2FzZSA9IHRydWVcbiAgICB9XG4gIH1cbn1cblxuLy8gVGhlIHByb3BlcnR5IGlzIHVzZWQgYnkgYEJ1ZmZlci5pc0J1ZmZlcmAgYW5kIGBpcy1idWZmZXJgIChpbiBTYWZhcmkgNS03KSB0byBkZXRlY3Rcbi8vIEJ1ZmZlciBpbnN0YW5jZXMuXG5CdWZmZXIucHJvdG90eXBlLl9pc0J1ZmZlciA9IHRydWVcblxuZnVuY3Rpb24gc3dhcCAoYiwgbiwgbSkge1xuICB2YXIgaSA9IGJbbl1cbiAgYltuXSA9IGJbbV1cbiAgYlttXSA9IGlcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5zd2FwMTYgPSBmdW5jdGlvbiBzd2FwMTYgKCkge1xuICB2YXIgbGVuID0gdGhpcy5sZW5ndGhcbiAgaWYgKGxlbiAlIDIgIT09IDApIHtcbiAgICB0aHJvdyBuZXcgUmFuZ2VFcnJvcignQnVmZmVyIHNpemUgbXVzdCBiZSBhIG11bHRpcGxlIG9mIDE2LWJpdHMnKVxuICB9XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGVuOyBpICs9IDIpIHtcbiAgICBzd2FwKHRoaXMsIGksIGkgKyAxKVxuICB9XG4gIHJldHVybiB0aGlzXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUuc3dhcDMyID0gZnVuY3Rpb24gc3dhcDMyICgpIHtcbiAgdmFyIGxlbiA9IHRoaXMubGVuZ3RoXG4gIGlmIChsZW4gJSA0ICE9PSAwKSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ0J1ZmZlciBzaXplIG11c3QgYmUgYSBtdWx0aXBsZSBvZiAzMi1iaXRzJylcbiAgfVxuICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbjsgaSArPSA0KSB7XG4gICAgc3dhcCh0aGlzLCBpLCBpICsgMylcbiAgICBzd2FwKHRoaXMsIGkgKyAxLCBpICsgMilcbiAgfVxuICByZXR1cm4gdGhpc1xufVxuXG5CdWZmZXIucHJvdG90eXBlLnN3YXA2NCA9IGZ1bmN0aW9uIHN3YXA2NCAoKSB7XG4gIHZhciBsZW4gPSB0aGlzLmxlbmd0aFxuICBpZiAobGVuICUgOCAhPT0gMCkge1xuICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdCdWZmZXIgc2l6ZSBtdXN0IGJlIGEgbXVsdGlwbGUgb2YgNjQtYml0cycpXG4gIH1cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBsZW47IGkgKz0gOCkge1xuICAgIHN3YXAodGhpcywgaSwgaSArIDcpXG4gICAgc3dhcCh0aGlzLCBpICsgMSwgaSArIDYpXG4gICAgc3dhcCh0aGlzLCBpICsgMiwgaSArIDUpXG4gICAgc3dhcCh0aGlzLCBpICsgMywgaSArIDQpXG4gIH1cbiAgcmV0dXJuIHRoaXNcbn1cblxuQnVmZmVyLnByb3RvdHlwZS50b1N0cmluZyA9IGZ1bmN0aW9uIHRvU3RyaW5nICgpIHtcbiAgdmFyIGxlbmd0aCA9IHRoaXMubGVuZ3RoIHwgMFxuICBpZiAobGVuZ3RoID09PSAwKSByZXR1cm4gJydcbiAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPT09IDApIHJldHVybiB1dGY4U2xpY2UodGhpcywgMCwgbGVuZ3RoKVxuICByZXR1cm4gc2xvd1RvU3RyaW5nLmFwcGx5KHRoaXMsIGFyZ3VtZW50cylcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5lcXVhbHMgPSBmdW5jdGlvbiBlcXVhbHMgKGIpIHtcbiAgaWYgKCFCdWZmZXIuaXNCdWZmZXIoYikpIHRocm93IG5ldyBUeXBlRXJyb3IoJ0FyZ3VtZW50IG11c3QgYmUgYSBCdWZmZXInKVxuICBpZiAodGhpcyA9PT0gYikgcmV0dXJuIHRydWVcbiAgcmV0dXJuIEJ1ZmZlci5jb21wYXJlKHRoaXMsIGIpID09PSAwXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUuaW5zcGVjdCA9IGZ1bmN0aW9uIGluc3BlY3QgKCkge1xuICB2YXIgc3RyID0gJydcbiAgdmFyIG1heCA9IGV4cG9ydHMuSU5TUEVDVF9NQVhfQllURVNcbiAgaWYgKHRoaXMubGVuZ3RoID4gMCkge1xuICAgIHN0ciA9IHRoaXMudG9TdHJpbmcoJ2hleCcsIDAsIG1heCkubWF0Y2goLy57Mn0vZykuam9pbignICcpXG4gICAgaWYgKHRoaXMubGVuZ3RoID4gbWF4KSBzdHIgKz0gJyAuLi4gJ1xuICB9XG4gIHJldHVybiAnPEJ1ZmZlciAnICsgc3RyICsgJz4nXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUuY29tcGFyZSA9IGZ1bmN0aW9uIGNvbXBhcmUgKHRhcmdldCwgc3RhcnQsIGVuZCwgdGhpc1N0YXJ0LCB0aGlzRW5kKSB7XG4gIGlmICghQnVmZmVyLmlzQnVmZmVyKHRhcmdldCkpIHtcbiAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdBcmd1bWVudCBtdXN0IGJlIGEgQnVmZmVyJylcbiAgfVxuXG4gIGlmIChzdGFydCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgc3RhcnQgPSAwXG4gIH1cbiAgaWYgKGVuZCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgZW5kID0gdGFyZ2V0ID8gdGFyZ2V0Lmxlbmd0aCA6IDBcbiAgfVxuICBpZiAodGhpc1N0YXJ0ID09PSB1bmRlZmluZWQpIHtcbiAgICB0aGlzU3RhcnQgPSAwXG4gIH1cbiAgaWYgKHRoaXNFbmQgPT09IHVuZGVmaW5lZCkge1xuICAgIHRoaXNFbmQgPSB0aGlzLmxlbmd0aFxuICB9XG5cbiAgaWYgKHN0YXJ0IDwgMCB8fCBlbmQgPiB0YXJnZXQubGVuZ3RoIHx8IHRoaXNTdGFydCA8IDAgfHwgdGhpc0VuZCA+IHRoaXMubGVuZ3RoKSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ291dCBvZiByYW5nZSBpbmRleCcpXG4gIH1cblxuICBpZiAodGhpc1N0YXJ0ID49IHRoaXNFbmQgJiYgc3RhcnQgPj0gZW5kKSB7XG4gICAgcmV0dXJuIDBcbiAgfVxuICBpZiAodGhpc1N0YXJ0ID49IHRoaXNFbmQpIHtcbiAgICByZXR1cm4gLTFcbiAgfVxuICBpZiAoc3RhcnQgPj0gZW5kKSB7XG4gICAgcmV0dXJuIDFcbiAgfVxuXG4gIHN0YXJ0ID4+Pj0gMFxuICBlbmQgPj4+PSAwXG4gIHRoaXNTdGFydCA+Pj49IDBcbiAgdGhpc0VuZCA+Pj49IDBcblxuICBpZiAodGhpcyA9PT0gdGFyZ2V0KSByZXR1cm4gMFxuXG4gIHZhciB4ID0gdGhpc0VuZCAtIHRoaXNTdGFydFxuICB2YXIgeSA9IGVuZCAtIHN0YXJ0XG4gIHZhciBsZW4gPSBNYXRoLm1pbih4LCB5KVxuXG4gIHZhciB0aGlzQ29weSA9IHRoaXMuc2xpY2UodGhpc1N0YXJ0LCB0aGlzRW5kKVxuICB2YXIgdGFyZ2V0Q29weSA9IHRhcmdldC5zbGljZShzdGFydCwgZW5kKVxuXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGVuOyArK2kpIHtcbiAgICBpZiAodGhpc0NvcHlbaV0gIT09IHRhcmdldENvcHlbaV0pIHtcbiAgICAgIHggPSB0aGlzQ29weVtpXVxuICAgICAgeSA9IHRhcmdldENvcHlbaV1cbiAgICAgIGJyZWFrXG4gICAgfVxuICB9XG5cbiAgaWYgKHggPCB5KSByZXR1cm4gLTFcbiAgaWYgKHkgPCB4KSByZXR1cm4gMVxuICByZXR1cm4gMFxufVxuXG4vLyBGaW5kcyBlaXRoZXIgdGhlIGZpcnN0IGluZGV4IG9mIGB2YWxgIGluIGBidWZmZXJgIGF0IG9mZnNldCA+PSBgYnl0ZU9mZnNldGAsXG4vLyBPUiB0aGUgbGFzdCBpbmRleCBvZiBgdmFsYCBpbiBgYnVmZmVyYCBhdCBvZmZzZXQgPD0gYGJ5dGVPZmZzZXRgLlxuLy9cbi8vIEFyZ3VtZW50czpcbi8vIC0gYnVmZmVyIC0gYSBCdWZmZXIgdG8gc2VhcmNoXG4vLyAtIHZhbCAtIGEgc3RyaW5nLCBCdWZmZXIsIG9yIG51bWJlclxuLy8gLSBieXRlT2Zmc2V0IC0gYW4gaW5kZXggaW50byBgYnVmZmVyYDsgd2lsbCBiZSBjbGFtcGVkIHRvIGFuIGludDMyXG4vLyAtIGVuY29kaW5nIC0gYW4gb3B0aW9uYWwgZW5jb2RpbmcsIHJlbGV2YW50IGlzIHZhbCBpcyBhIHN0cmluZ1xuLy8gLSBkaXIgLSB0cnVlIGZvciBpbmRleE9mLCBmYWxzZSBmb3IgbGFzdEluZGV4T2ZcbmZ1bmN0aW9uIGJpZGlyZWN0aW9uYWxJbmRleE9mIChidWZmZXIsIHZhbCwgYnl0ZU9mZnNldCwgZW5jb2RpbmcsIGRpcikge1xuICAvLyBFbXB0eSBidWZmZXIgbWVhbnMgbm8gbWF0Y2hcbiAgaWYgKGJ1ZmZlci5sZW5ndGggPT09IDApIHJldHVybiAtMVxuXG4gIC8vIE5vcm1hbGl6ZSBieXRlT2Zmc2V0XG4gIGlmICh0eXBlb2YgYnl0ZU9mZnNldCA9PT0gJ3N0cmluZycpIHtcbiAgICBlbmNvZGluZyA9IGJ5dGVPZmZzZXRcbiAgICBieXRlT2Zmc2V0ID0gMFxuICB9IGVsc2UgaWYgKGJ5dGVPZmZzZXQgPiAweDdmZmZmZmZmKSB7XG4gICAgYnl0ZU9mZnNldCA9IDB4N2ZmZmZmZmZcbiAgfSBlbHNlIGlmIChieXRlT2Zmc2V0IDwgLTB4ODAwMDAwMDApIHtcbiAgICBieXRlT2Zmc2V0ID0gLTB4ODAwMDAwMDBcbiAgfVxuICBieXRlT2Zmc2V0ID0gK2J5dGVPZmZzZXQgIC8vIENvZXJjZSB0byBOdW1iZXIuXG4gIGlmIChpc05hTihieXRlT2Zmc2V0KSkge1xuICAgIC8vIGJ5dGVPZmZzZXQ6IGl0IGl0J3MgdW5kZWZpbmVkLCBudWxsLCBOYU4sIFwiZm9vXCIsIGV0Yywgc2VhcmNoIHdob2xlIGJ1ZmZlclxuICAgIGJ5dGVPZmZzZXQgPSBkaXIgPyAwIDogKGJ1ZmZlci5sZW5ndGggLSAxKVxuICB9XG5cbiAgLy8gTm9ybWFsaXplIGJ5dGVPZmZzZXQ6IG5lZ2F0aXZlIG9mZnNldHMgc3RhcnQgZnJvbSB0aGUgZW5kIG9mIHRoZSBidWZmZXJcbiAgaWYgKGJ5dGVPZmZzZXQgPCAwKSBieXRlT2Zmc2V0ID0gYnVmZmVyLmxlbmd0aCArIGJ5dGVPZmZzZXRcbiAgaWYgKGJ5dGVPZmZzZXQgPj0gYnVmZmVyLmxlbmd0aCkge1xuICAgIGlmIChkaXIpIHJldHVybiAtMVxuICAgIGVsc2UgYnl0ZU9mZnNldCA9IGJ1ZmZlci5sZW5ndGggLSAxXG4gIH0gZWxzZSBpZiAoYnl0ZU9mZnNldCA8IDApIHtcbiAgICBpZiAoZGlyKSBieXRlT2Zmc2V0ID0gMFxuICAgIGVsc2UgcmV0dXJuIC0xXG4gIH1cblxuICAvLyBOb3JtYWxpemUgdmFsXG4gIGlmICh0eXBlb2YgdmFsID09PSAnc3RyaW5nJykge1xuICAgIHZhbCA9IEJ1ZmZlci5mcm9tKHZhbCwgZW5jb2RpbmcpXG4gIH1cblxuICAvLyBGaW5hbGx5LCBzZWFyY2ggZWl0aGVyIGluZGV4T2YgKGlmIGRpciBpcyB0cnVlKSBvciBsYXN0SW5kZXhPZlxuICBpZiAoQnVmZmVyLmlzQnVmZmVyKHZhbCkpIHtcbiAgICAvLyBTcGVjaWFsIGNhc2U6IGxvb2tpbmcgZm9yIGVtcHR5IHN0cmluZy9idWZmZXIgYWx3YXlzIGZhaWxzXG4gICAgaWYgKHZhbC5sZW5ndGggPT09IDApIHtcbiAgICAgIHJldHVybiAtMVxuICAgIH1cbiAgICByZXR1cm4gYXJyYXlJbmRleE9mKGJ1ZmZlciwgdmFsLCBieXRlT2Zmc2V0LCBlbmNvZGluZywgZGlyKVxuICB9IGVsc2UgaWYgKHR5cGVvZiB2YWwgPT09ICdudW1iZXInKSB7XG4gICAgdmFsID0gdmFsICYgMHhGRiAvLyBTZWFyY2ggZm9yIGEgYnl0ZSB2YWx1ZSBbMC0yNTVdXG4gICAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUICYmXG4gICAgICAgIHR5cGVvZiBVaW50OEFycmF5LnByb3RvdHlwZS5pbmRleE9mID09PSAnZnVuY3Rpb24nKSB7XG4gICAgICBpZiAoZGlyKSB7XG4gICAgICAgIHJldHVybiBVaW50OEFycmF5LnByb3RvdHlwZS5pbmRleE9mLmNhbGwoYnVmZmVyLCB2YWwsIGJ5dGVPZmZzZXQpXG4gICAgICB9IGVsc2Uge1xuICAgICAgICByZXR1cm4gVWludDhBcnJheS5wcm90b3R5cGUubGFzdEluZGV4T2YuY2FsbChidWZmZXIsIHZhbCwgYnl0ZU9mZnNldClcbiAgICAgIH1cbiAgICB9XG4gICAgcmV0dXJuIGFycmF5SW5kZXhPZihidWZmZXIsIFsgdmFsIF0sIGJ5dGVPZmZzZXQsIGVuY29kaW5nLCBkaXIpXG4gIH1cblxuICB0aHJvdyBuZXcgVHlwZUVycm9yKCd2YWwgbXVzdCBiZSBzdHJpbmcsIG51bWJlciBvciBCdWZmZXInKVxufVxuXG5mdW5jdGlvbiBhcnJheUluZGV4T2YgKGFyciwgdmFsLCBieXRlT2Zmc2V0LCBlbmNvZGluZywgZGlyKSB7XG4gIHZhciBpbmRleFNpemUgPSAxXG4gIHZhciBhcnJMZW5ndGggPSBhcnIubGVuZ3RoXG4gIHZhciB2YWxMZW5ndGggPSB2YWwubGVuZ3RoXG5cbiAgaWYgKGVuY29kaW5nICE9PSB1bmRlZmluZWQpIHtcbiAgICBlbmNvZGluZyA9IFN0cmluZyhlbmNvZGluZykudG9Mb3dlckNhc2UoKVxuICAgIGlmIChlbmNvZGluZyA9PT0gJ3VjczInIHx8IGVuY29kaW5nID09PSAndWNzLTInIHx8XG4gICAgICAgIGVuY29kaW5nID09PSAndXRmMTZsZScgfHwgZW5jb2RpbmcgPT09ICd1dGYtMTZsZScpIHtcbiAgICAgIGlmIChhcnIubGVuZ3RoIDwgMiB8fCB2YWwubGVuZ3RoIDwgMikge1xuICAgICAgICByZXR1cm4gLTFcbiAgICAgIH1cbiAgICAgIGluZGV4U2l6ZSA9IDJcbiAgICAgIGFyckxlbmd0aCAvPSAyXG4gICAgICB2YWxMZW5ndGggLz0gMlxuICAgICAgYnl0ZU9mZnNldCAvPSAyXG4gICAgfVxuICB9XG5cbiAgZnVuY3Rpb24gcmVhZCAoYnVmLCBpKSB7XG4gICAgaWYgKGluZGV4U2l6ZSA9PT0gMSkge1xuICAgICAgcmV0dXJuIGJ1ZltpXVxuICAgIH0gZWxzZSB7XG4gICAgICByZXR1cm4gYnVmLnJlYWRVSW50MTZCRShpICogaW5kZXhTaXplKVxuICAgIH1cbiAgfVxuXG4gIHZhciBpXG4gIGlmIChkaXIpIHtcbiAgICB2YXIgZm91bmRJbmRleCA9IC0xXG4gICAgZm9yIChpID0gYnl0ZU9mZnNldDsgaSA8IGFyckxlbmd0aDsgaSsrKSB7XG4gICAgICBpZiAocmVhZChhcnIsIGkpID09PSByZWFkKHZhbCwgZm91bmRJbmRleCA9PT0gLTEgPyAwIDogaSAtIGZvdW5kSW5kZXgpKSB7XG4gICAgICAgIGlmIChmb3VuZEluZGV4ID09PSAtMSkgZm91bmRJbmRleCA9IGlcbiAgICAgICAgaWYgKGkgLSBmb3VuZEluZGV4ICsgMSA9PT0gdmFsTGVuZ3RoKSByZXR1cm4gZm91bmRJbmRleCAqIGluZGV4U2l6ZVxuICAgICAgfSBlbHNlIHtcbiAgICAgICAgaWYgKGZvdW5kSW5kZXggIT09IC0xKSBpIC09IGkgLSBmb3VuZEluZGV4XG4gICAgICAgIGZvdW5kSW5kZXggPSAtMVxuICAgICAgfVxuICAgIH1cbiAgfSBlbHNlIHtcbiAgICBpZiAoYnl0ZU9mZnNldCArIHZhbExlbmd0aCA+IGFyckxlbmd0aCkgYnl0ZU9mZnNldCA9IGFyckxlbmd0aCAtIHZhbExlbmd0aFxuICAgIGZvciAoaSA9IGJ5dGVPZmZzZXQ7IGkgPj0gMDsgaS0tKSB7XG4gICAgICB2YXIgZm91bmQgPSB0cnVlXG4gICAgICBmb3IgKHZhciBqID0gMDsgaiA8IHZhbExlbmd0aDsgaisrKSB7XG4gICAgICAgIGlmIChyZWFkKGFyciwgaSArIGopICE9PSByZWFkKHZhbCwgaikpIHtcbiAgICAgICAgICBmb3VuZCA9IGZhbHNlXG4gICAgICAgICAgYnJlYWtcbiAgICAgICAgfVxuICAgICAgfVxuICAgICAgaWYgKGZvdW5kKSByZXR1cm4gaVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiAtMVxufVxuXG5CdWZmZXIucHJvdG90eXBlLmluY2x1ZGVzID0gZnVuY3Rpb24gaW5jbHVkZXMgKHZhbCwgYnl0ZU9mZnNldCwgZW5jb2RpbmcpIHtcbiAgcmV0dXJuIHRoaXMuaW5kZXhPZih2YWwsIGJ5dGVPZmZzZXQsIGVuY29kaW5nKSAhPT0gLTFcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5pbmRleE9mID0gZnVuY3Rpb24gaW5kZXhPZiAodmFsLCBieXRlT2Zmc2V0LCBlbmNvZGluZykge1xuICByZXR1cm4gYmlkaXJlY3Rpb25hbEluZGV4T2YodGhpcywgdmFsLCBieXRlT2Zmc2V0LCBlbmNvZGluZywgdHJ1ZSlcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5sYXN0SW5kZXhPZiA9IGZ1bmN0aW9uIGxhc3RJbmRleE9mICh2YWwsIGJ5dGVPZmZzZXQsIGVuY29kaW5nKSB7XG4gIHJldHVybiBiaWRpcmVjdGlvbmFsSW5kZXhPZih0aGlzLCB2YWwsIGJ5dGVPZmZzZXQsIGVuY29kaW5nLCBmYWxzZSlcbn1cblxuZnVuY3Rpb24gaGV4V3JpdGUgKGJ1Ziwgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aCkge1xuICBvZmZzZXQgPSBOdW1iZXIob2Zmc2V0KSB8fCAwXG4gIHZhciByZW1haW5pbmcgPSBidWYubGVuZ3RoIC0gb2Zmc2V0XG4gIGlmICghbGVuZ3RoKSB7XG4gICAgbGVuZ3RoID0gcmVtYWluaW5nXG4gIH0gZWxzZSB7XG4gICAgbGVuZ3RoID0gTnVtYmVyKGxlbmd0aClcbiAgICBpZiAobGVuZ3RoID4gcmVtYWluaW5nKSB7XG4gICAgICBsZW5ndGggPSByZW1haW5pbmdcbiAgICB9XG4gIH1cblxuICAvLyBtdXN0IGJlIGFuIGV2ZW4gbnVtYmVyIG9mIGRpZ2l0c1xuICB2YXIgc3RyTGVuID0gc3RyaW5nLmxlbmd0aFxuICBpZiAoc3RyTGVuICUgMiAhPT0gMCkgdGhyb3cgbmV3IFR5cGVFcnJvcignSW52YWxpZCBoZXggc3RyaW5nJylcblxuICBpZiAobGVuZ3RoID4gc3RyTGVuIC8gMikge1xuICAgIGxlbmd0aCA9IHN0ckxlbiAvIDJcbiAgfVxuICBmb3IgKHZhciBpID0gMDsgaSA8IGxlbmd0aDsgKytpKSB7XG4gICAgdmFyIHBhcnNlZCA9IHBhcnNlSW50KHN0cmluZy5zdWJzdHIoaSAqIDIsIDIpLCAxNilcbiAgICBpZiAoaXNOYU4ocGFyc2VkKSkgcmV0dXJuIGlcbiAgICBidWZbb2Zmc2V0ICsgaV0gPSBwYXJzZWRcbiAgfVxuICByZXR1cm4gaVxufVxuXG5mdW5jdGlvbiB1dGY4V3JpdGUgKGJ1Ziwgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aCkge1xuICByZXR1cm4gYmxpdEJ1ZmZlcih1dGY4VG9CeXRlcyhzdHJpbmcsIGJ1Zi5sZW5ndGggLSBvZmZzZXQpLCBidWYsIG9mZnNldCwgbGVuZ3RoKVxufVxuXG5mdW5jdGlvbiBhc2NpaVdyaXRlIChidWYsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpIHtcbiAgcmV0dXJuIGJsaXRCdWZmZXIoYXNjaWlUb0J5dGVzKHN0cmluZyksIGJ1Ziwgb2Zmc2V0LCBsZW5ndGgpXG59XG5cbmZ1bmN0aW9uIGxhdGluMVdyaXRlIChidWYsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpIHtcbiAgcmV0dXJuIGFzY2lpV3JpdGUoYnVmLCBzdHJpbmcsIG9mZnNldCwgbGVuZ3RoKVxufVxuXG5mdW5jdGlvbiBiYXNlNjRXcml0ZSAoYnVmLCBzdHJpbmcsIG9mZnNldCwgbGVuZ3RoKSB7XG4gIHJldHVybiBibGl0QnVmZmVyKGJhc2U2NFRvQnl0ZXMoc3RyaW5nKSwgYnVmLCBvZmZzZXQsIGxlbmd0aClcbn1cblxuZnVuY3Rpb24gdWNzMldyaXRlIChidWYsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpIHtcbiAgcmV0dXJuIGJsaXRCdWZmZXIodXRmMTZsZVRvQnl0ZXMoc3RyaW5nLCBidWYubGVuZ3RoIC0gb2Zmc2V0KSwgYnVmLCBvZmZzZXQsIGxlbmd0aClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZSA9IGZ1bmN0aW9uIHdyaXRlIChzdHJpbmcsIG9mZnNldCwgbGVuZ3RoLCBlbmNvZGluZykge1xuICAvLyBCdWZmZXIjd3JpdGUoc3RyaW5nKVxuICBpZiAob2Zmc2V0ID09PSB1bmRlZmluZWQpIHtcbiAgICBlbmNvZGluZyA9ICd1dGY4J1xuICAgIGxlbmd0aCA9IHRoaXMubGVuZ3RoXG4gICAgb2Zmc2V0ID0gMFxuICAvLyBCdWZmZXIjd3JpdGUoc3RyaW5nLCBlbmNvZGluZylcbiAgfSBlbHNlIGlmIChsZW5ndGggPT09IHVuZGVmaW5lZCAmJiB0eXBlb2Ygb2Zmc2V0ID09PSAnc3RyaW5nJykge1xuICAgIGVuY29kaW5nID0gb2Zmc2V0XG4gICAgbGVuZ3RoID0gdGhpcy5sZW5ndGhcbiAgICBvZmZzZXQgPSAwXG4gIC8vIEJ1ZmZlciN3cml0ZShzdHJpbmcsIG9mZnNldFssIGxlbmd0aF1bLCBlbmNvZGluZ10pXG4gIH0gZWxzZSBpZiAoaXNGaW5pdGUob2Zmc2V0KSkge1xuICAgIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgICBpZiAoaXNGaW5pdGUobGVuZ3RoKSkge1xuICAgICAgbGVuZ3RoID0gbGVuZ3RoIHwgMFxuICAgICAgaWYgKGVuY29kaW5nID09PSB1bmRlZmluZWQpIGVuY29kaW5nID0gJ3V0ZjgnXG4gICAgfSBlbHNlIHtcbiAgICAgIGVuY29kaW5nID0gbGVuZ3RoXG4gICAgICBsZW5ndGggPSB1bmRlZmluZWRcbiAgICB9XG4gIC8vIGxlZ2FjeSB3cml0ZShzdHJpbmcsIGVuY29kaW5nLCBvZmZzZXQsIGxlbmd0aCkgLSByZW1vdmUgaW4gdjAuMTNcbiAgfSBlbHNlIHtcbiAgICB0aHJvdyBuZXcgRXJyb3IoXG4gICAgICAnQnVmZmVyLndyaXRlKHN0cmluZywgZW5jb2RpbmcsIG9mZnNldFssIGxlbmd0aF0pIGlzIG5vIGxvbmdlciBzdXBwb3J0ZWQnXG4gICAgKVxuICB9XG5cbiAgdmFyIHJlbWFpbmluZyA9IHRoaXMubGVuZ3RoIC0gb2Zmc2V0XG4gIGlmIChsZW5ndGggPT09IHVuZGVmaW5lZCB8fCBsZW5ndGggPiByZW1haW5pbmcpIGxlbmd0aCA9IHJlbWFpbmluZ1xuXG4gIGlmICgoc3RyaW5nLmxlbmd0aCA+IDAgJiYgKGxlbmd0aCA8IDAgfHwgb2Zmc2V0IDwgMCkpIHx8IG9mZnNldCA+IHRoaXMubGVuZ3RoKSB7XG4gICAgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ0F0dGVtcHQgdG8gd3JpdGUgb3V0c2lkZSBidWZmZXIgYm91bmRzJylcbiAgfVxuXG4gIGlmICghZW5jb2RpbmcpIGVuY29kaW5nID0gJ3V0ZjgnXG5cbiAgdmFyIGxvd2VyZWRDYXNlID0gZmFsc2VcbiAgZm9yICg7Oykge1xuICAgIHN3aXRjaCAoZW5jb2RpbmcpIHtcbiAgICAgIGNhc2UgJ2hleCc6XG4gICAgICAgIHJldHVybiBoZXhXcml0ZSh0aGlzLCBzdHJpbmcsIG9mZnNldCwgbGVuZ3RoKVxuXG4gICAgICBjYXNlICd1dGY4JzpcbiAgICAgIGNhc2UgJ3V0Zi04JzpcbiAgICAgICAgcmV0dXJuIHV0ZjhXcml0ZSh0aGlzLCBzdHJpbmcsIG9mZnNldCwgbGVuZ3RoKVxuXG4gICAgICBjYXNlICdhc2NpaSc6XG4gICAgICAgIHJldHVybiBhc2NpaVdyaXRlKHRoaXMsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpXG5cbiAgICAgIGNhc2UgJ2xhdGluMSc6XG4gICAgICBjYXNlICdiaW5hcnknOlxuICAgICAgICByZXR1cm4gbGF0aW4xV3JpdGUodGhpcywgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aClcblxuICAgICAgY2FzZSAnYmFzZTY0JzpcbiAgICAgICAgLy8gV2FybmluZzogbWF4TGVuZ3RoIG5vdCB0YWtlbiBpbnRvIGFjY291bnQgaW4gYmFzZTY0V3JpdGVcbiAgICAgICAgcmV0dXJuIGJhc2U2NFdyaXRlKHRoaXMsIHN0cmluZywgb2Zmc2V0LCBsZW5ndGgpXG5cbiAgICAgIGNhc2UgJ3VjczInOlxuICAgICAgY2FzZSAndWNzLTInOlxuICAgICAgY2FzZSAndXRmMTZsZSc6XG4gICAgICBjYXNlICd1dGYtMTZsZSc6XG4gICAgICAgIHJldHVybiB1Y3MyV3JpdGUodGhpcywgc3RyaW5nLCBvZmZzZXQsIGxlbmd0aClcblxuICAgICAgZGVmYXVsdDpcbiAgICAgICAgaWYgKGxvd2VyZWRDYXNlKSB0aHJvdyBuZXcgVHlwZUVycm9yKCdVbmtub3duIGVuY29kaW5nOiAnICsgZW5jb2RpbmcpXG4gICAgICAgIGVuY29kaW5nID0gKCcnICsgZW5jb2RpbmcpLnRvTG93ZXJDYXNlKClcbiAgICAgICAgbG93ZXJlZENhc2UgPSB0cnVlXG4gICAgfVxuICB9XG59XG5cbkJ1ZmZlci5wcm90b3R5cGUudG9KU09OID0gZnVuY3Rpb24gdG9KU09OICgpIHtcbiAgcmV0dXJuIHtcbiAgICB0eXBlOiAnQnVmZmVyJyxcbiAgICBkYXRhOiBBcnJheS5wcm90b3R5cGUuc2xpY2UuY2FsbCh0aGlzLl9hcnIgfHwgdGhpcywgMClcbiAgfVxufVxuXG5mdW5jdGlvbiBiYXNlNjRTbGljZSAoYnVmLCBzdGFydCwgZW5kKSB7XG4gIGlmIChzdGFydCA9PT0gMCAmJiBlbmQgPT09IGJ1Zi5sZW5ndGgpIHtcbiAgICByZXR1cm4gYmFzZTY0LmZyb21CeXRlQXJyYXkoYnVmKVxuICB9IGVsc2Uge1xuICAgIHJldHVybiBiYXNlNjQuZnJvbUJ5dGVBcnJheShidWYuc2xpY2Uoc3RhcnQsIGVuZCkpXG4gIH1cbn1cblxuZnVuY3Rpb24gdXRmOFNsaWNlIChidWYsIHN0YXJ0LCBlbmQpIHtcbiAgZW5kID0gTWF0aC5taW4oYnVmLmxlbmd0aCwgZW5kKVxuICB2YXIgcmVzID0gW11cblxuICB2YXIgaSA9IHN0YXJ0XG4gIHdoaWxlIChpIDwgZW5kKSB7XG4gICAgdmFyIGZpcnN0Qnl0ZSA9IGJ1ZltpXVxuICAgIHZhciBjb2RlUG9pbnQgPSBudWxsXG4gICAgdmFyIGJ5dGVzUGVyU2VxdWVuY2UgPSAoZmlyc3RCeXRlID4gMHhFRikgPyA0XG4gICAgICA6IChmaXJzdEJ5dGUgPiAweERGKSA/IDNcbiAgICAgIDogKGZpcnN0Qnl0ZSA+IDB4QkYpID8gMlxuICAgICAgOiAxXG5cbiAgICBpZiAoaSArIGJ5dGVzUGVyU2VxdWVuY2UgPD0gZW5kKSB7XG4gICAgICB2YXIgc2Vjb25kQnl0ZSwgdGhpcmRCeXRlLCBmb3VydGhCeXRlLCB0ZW1wQ29kZVBvaW50XG5cbiAgICAgIHN3aXRjaCAoYnl0ZXNQZXJTZXF1ZW5jZSkge1xuICAgICAgICBjYXNlIDE6XG4gICAgICAgICAgaWYgKGZpcnN0Qnl0ZSA8IDB4ODApIHtcbiAgICAgICAgICAgIGNvZGVQb2ludCA9IGZpcnN0Qnl0ZVxuICAgICAgICAgIH1cbiAgICAgICAgICBicmVha1xuICAgICAgICBjYXNlIDI6XG4gICAgICAgICAgc2Vjb25kQnl0ZSA9IGJ1ZltpICsgMV1cbiAgICAgICAgICBpZiAoKHNlY29uZEJ5dGUgJiAweEMwKSA9PT0gMHg4MCkge1xuICAgICAgICAgICAgdGVtcENvZGVQb2ludCA9IChmaXJzdEJ5dGUgJiAweDFGKSA8PCAweDYgfCAoc2Vjb25kQnl0ZSAmIDB4M0YpXG4gICAgICAgICAgICBpZiAodGVtcENvZGVQb2ludCA+IDB4N0YpIHtcbiAgICAgICAgICAgICAgY29kZVBvaW50ID0gdGVtcENvZGVQb2ludFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBicmVha1xuICAgICAgICBjYXNlIDM6XG4gICAgICAgICAgc2Vjb25kQnl0ZSA9IGJ1ZltpICsgMV1cbiAgICAgICAgICB0aGlyZEJ5dGUgPSBidWZbaSArIDJdXG4gICAgICAgICAgaWYgKChzZWNvbmRCeXRlICYgMHhDMCkgPT09IDB4ODAgJiYgKHRoaXJkQnl0ZSAmIDB4QzApID09PSAweDgwKSB7XG4gICAgICAgICAgICB0ZW1wQ29kZVBvaW50ID0gKGZpcnN0Qnl0ZSAmIDB4RikgPDwgMHhDIHwgKHNlY29uZEJ5dGUgJiAweDNGKSA8PCAweDYgfCAodGhpcmRCeXRlICYgMHgzRilcbiAgICAgICAgICAgIGlmICh0ZW1wQ29kZVBvaW50ID4gMHg3RkYgJiYgKHRlbXBDb2RlUG9pbnQgPCAweEQ4MDAgfHwgdGVtcENvZGVQb2ludCA+IDB4REZGRikpIHtcbiAgICAgICAgICAgICAgY29kZVBvaW50ID0gdGVtcENvZGVQb2ludFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBicmVha1xuICAgICAgICBjYXNlIDQ6XG4gICAgICAgICAgc2Vjb25kQnl0ZSA9IGJ1ZltpICsgMV1cbiAgICAgICAgICB0aGlyZEJ5dGUgPSBidWZbaSArIDJdXG4gICAgICAgICAgZm91cnRoQnl0ZSA9IGJ1ZltpICsgM11cbiAgICAgICAgICBpZiAoKHNlY29uZEJ5dGUgJiAweEMwKSA9PT0gMHg4MCAmJiAodGhpcmRCeXRlICYgMHhDMCkgPT09IDB4ODAgJiYgKGZvdXJ0aEJ5dGUgJiAweEMwKSA9PT0gMHg4MCkge1xuICAgICAgICAgICAgdGVtcENvZGVQb2ludCA9IChmaXJzdEJ5dGUgJiAweEYpIDw8IDB4MTIgfCAoc2Vjb25kQnl0ZSAmIDB4M0YpIDw8IDB4QyB8ICh0aGlyZEJ5dGUgJiAweDNGKSA8PCAweDYgfCAoZm91cnRoQnl0ZSAmIDB4M0YpXG4gICAgICAgICAgICBpZiAodGVtcENvZGVQb2ludCA+IDB4RkZGRiAmJiB0ZW1wQ29kZVBvaW50IDwgMHgxMTAwMDApIHtcbiAgICAgICAgICAgICAgY29kZVBvaW50ID0gdGVtcENvZGVQb2ludFxuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG5cbiAgICBpZiAoY29kZVBvaW50ID09PSBudWxsKSB7XG4gICAgICAvLyB3ZSBkaWQgbm90IGdlbmVyYXRlIGEgdmFsaWQgY29kZVBvaW50IHNvIGluc2VydCBhXG4gICAgICAvLyByZXBsYWNlbWVudCBjaGFyIChVK0ZGRkQpIGFuZCBhZHZhbmNlIG9ubHkgMSBieXRlXG4gICAgICBjb2RlUG9pbnQgPSAweEZGRkRcbiAgICAgIGJ5dGVzUGVyU2VxdWVuY2UgPSAxXG4gICAgfSBlbHNlIGlmIChjb2RlUG9pbnQgPiAweEZGRkYpIHtcbiAgICAgIC8vIGVuY29kZSB0byB1dGYxNiAoc3Vycm9nYXRlIHBhaXIgZGFuY2UpXG4gICAgICBjb2RlUG9pbnQgLT0gMHgxMDAwMFxuICAgICAgcmVzLnB1c2goY29kZVBvaW50ID4+PiAxMCAmIDB4M0ZGIHwgMHhEODAwKVxuICAgICAgY29kZVBvaW50ID0gMHhEQzAwIHwgY29kZVBvaW50ICYgMHgzRkZcbiAgICB9XG5cbiAgICByZXMucHVzaChjb2RlUG9pbnQpXG4gICAgaSArPSBieXRlc1BlclNlcXVlbmNlXG4gIH1cblxuICByZXR1cm4gZGVjb2RlQ29kZVBvaW50c0FycmF5KHJlcylcbn1cblxuLy8gQmFzZWQgb24gaHR0cDovL3N0YWNrb3ZlcmZsb3cuY29tL2EvMjI3NDcyNzIvNjgwNzQyLCB0aGUgYnJvd3NlciB3aXRoXG4vLyB0aGUgbG93ZXN0IGxpbWl0IGlzIENocm9tZSwgd2l0aCAweDEwMDAwIGFyZ3MuXG4vLyBXZSBnbyAxIG1hZ25pdHVkZSBsZXNzLCBmb3Igc2FmZXR5XG52YXIgTUFYX0FSR1VNRU5UU19MRU5HVEggPSAweDEwMDBcblxuZnVuY3Rpb24gZGVjb2RlQ29kZVBvaW50c0FycmF5IChjb2RlUG9pbnRzKSB7XG4gIHZhciBsZW4gPSBjb2RlUG9pbnRzLmxlbmd0aFxuICBpZiAobGVuIDw9IE1BWF9BUkdVTUVOVFNfTEVOR1RIKSB7XG4gICAgcmV0dXJuIFN0cmluZy5mcm9tQ2hhckNvZGUuYXBwbHkoU3RyaW5nLCBjb2RlUG9pbnRzKSAvLyBhdm9pZCBleHRyYSBzbGljZSgpXG4gIH1cblxuICAvLyBEZWNvZGUgaW4gY2h1bmtzIHRvIGF2b2lkIFwiY2FsbCBzdGFjayBzaXplIGV4Y2VlZGVkXCIuXG4gIHZhciByZXMgPSAnJ1xuICB2YXIgaSA9IDBcbiAgd2hpbGUgKGkgPCBsZW4pIHtcbiAgICByZXMgKz0gU3RyaW5nLmZyb21DaGFyQ29kZS5hcHBseShcbiAgICAgIFN0cmluZyxcbiAgICAgIGNvZGVQb2ludHMuc2xpY2UoaSwgaSArPSBNQVhfQVJHVU1FTlRTX0xFTkdUSClcbiAgICApXG4gIH1cbiAgcmV0dXJuIHJlc1xufVxuXG5mdW5jdGlvbiBhc2NpaVNsaWNlIChidWYsIHN0YXJ0LCBlbmQpIHtcbiAgdmFyIHJldCA9ICcnXG4gIGVuZCA9IE1hdGgubWluKGJ1Zi5sZW5ndGgsIGVuZClcblxuICBmb3IgKHZhciBpID0gc3RhcnQ7IGkgPCBlbmQ7ICsraSkge1xuICAgIHJldCArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGJ1ZltpXSAmIDB4N0YpXG4gIH1cbiAgcmV0dXJuIHJldFxufVxuXG5mdW5jdGlvbiBsYXRpbjFTbGljZSAoYnVmLCBzdGFydCwgZW5kKSB7XG4gIHZhciByZXQgPSAnJ1xuICBlbmQgPSBNYXRoLm1pbihidWYubGVuZ3RoLCBlbmQpXG5cbiAgZm9yICh2YXIgaSA9IHN0YXJ0OyBpIDwgZW5kOyArK2kpIHtcbiAgICByZXQgKz0gU3RyaW5nLmZyb21DaGFyQ29kZShidWZbaV0pXG4gIH1cbiAgcmV0dXJuIHJldFxufVxuXG5mdW5jdGlvbiBoZXhTbGljZSAoYnVmLCBzdGFydCwgZW5kKSB7XG4gIHZhciBsZW4gPSBidWYubGVuZ3RoXG5cbiAgaWYgKCFzdGFydCB8fCBzdGFydCA8IDApIHN0YXJ0ID0gMFxuICBpZiAoIWVuZCB8fCBlbmQgPCAwIHx8IGVuZCA+IGxlbikgZW5kID0gbGVuXG5cbiAgdmFyIG91dCA9ICcnXG4gIGZvciAodmFyIGkgPSBzdGFydDsgaSA8IGVuZDsgKytpKSB7XG4gICAgb3V0ICs9IHRvSGV4KGJ1ZltpXSlcbiAgfVxuICByZXR1cm4gb3V0XG59XG5cbmZ1bmN0aW9uIHV0ZjE2bGVTbGljZSAoYnVmLCBzdGFydCwgZW5kKSB7XG4gIHZhciBieXRlcyA9IGJ1Zi5zbGljZShzdGFydCwgZW5kKVxuICB2YXIgcmVzID0gJydcbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBieXRlcy5sZW5ndGg7IGkgKz0gMikge1xuICAgIHJlcyArPSBTdHJpbmcuZnJvbUNoYXJDb2RlKGJ5dGVzW2ldICsgYnl0ZXNbaSArIDFdICogMjU2KVxuICB9XG4gIHJldHVybiByZXNcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5zbGljZSA9IGZ1bmN0aW9uIHNsaWNlIChzdGFydCwgZW5kKSB7XG4gIHZhciBsZW4gPSB0aGlzLmxlbmd0aFxuICBzdGFydCA9IH5+c3RhcnRcbiAgZW5kID0gZW5kID09PSB1bmRlZmluZWQgPyBsZW4gOiB+fmVuZFxuXG4gIGlmIChzdGFydCA8IDApIHtcbiAgICBzdGFydCArPSBsZW5cbiAgICBpZiAoc3RhcnQgPCAwKSBzdGFydCA9IDBcbiAgfSBlbHNlIGlmIChzdGFydCA+IGxlbikge1xuICAgIHN0YXJ0ID0gbGVuXG4gIH1cblxuICBpZiAoZW5kIDwgMCkge1xuICAgIGVuZCArPSBsZW5cbiAgICBpZiAoZW5kIDwgMCkgZW5kID0gMFxuICB9IGVsc2UgaWYgKGVuZCA+IGxlbikge1xuICAgIGVuZCA9IGxlblxuICB9XG5cbiAgaWYgKGVuZCA8IHN0YXJ0KSBlbmQgPSBzdGFydFxuXG4gIHZhciBuZXdCdWZcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgbmV3QnVmID0gdGhpcy5zdWJhcnJheShzdGFydCwgZW5kKVxuICAgIG5ld0J1Zi5fX3Byb3RvX18gPSBCdWZmZXIucHJvdG90eXBlXG4gIH0gZWxzZSB7XG4gICAgdmFyIHNsaWNlTGVuID0gZW5kIC0gc3RhcnRcbiAgICBuZXdCdWYgPSBuZXcgQnVmZmVyKHNsaWNlTGVuLCB1bmRlZmluZWQpXG4gICAgZm9yICh2YXIgaSA9IDA7IGkgPCBzbGljZUxlbjsgKytpKSB7XG4gICAgICBuZXdCdWZbaV0gPSB0aGlzW2kgKyBzdGFydF1cbiAgICB9XG4gIH1cblxuICByZXR1cm4gbmV3QnVmXG59XG5cbi8qXG4gKiBOZWVkIHRvIG1ha2Ugc3VyZSB0aGF0IGJ1ZmZlciBpc24ndCB0cnlpbmcgdG8gd3JpdGUgb3V0IG9mIGJvdW5kcy5cbiAqL1xuZnVuY3Rpb24gY2hlY2tPZmZzZXQgKG9mZnNldCwgZXh0LCBsZW5ndGgpIHtcbiAgaWYgKChvZmZzZXQgJSAxKSAhPT0gMCB8fCBvZmZzZXQgPCAwKSB0aHJvdyBuZXcgUmFuZ2VFcnJvcignb2Zmc2V0IGlzIG5vdCB1aW50JylcbiAgaWYgKG9mZnNldCArIGV4dCA+IGxlbmd0aCkgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ1RyeWluZyB0byBhY2Nlc3MgYmV5b25kIGJ1ZmZlciBsZW5ndGgnKVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRVSW50TEUgPSBmdW5jdGlvbiByZWFkVUludExFIChvZmZzZXQsIGJ5dGVMZW5ndGgsIG5vQXNzZXJ0KSB7XG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGggfCAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgYnl0ZUxlbmd0aCwgdGhpcy5sZW5ndGgpXG5cbiAgdmFyIHZhbCA9IHRoaXNbb2Zmc2V0XVxuICB2YXIgbXVsID0gMVxuICB2YXIgaSA9IDBcbiAgd2hpbGUgKCsraSA8IGJ5dGVMZW5ndGggJiYgKG11bCAqPSAweDEwMCkpIHtcbiAgICB2YWwgKz0gdGhpc1tvZmZzZXQgKyBpXSAqIG11bFxuICB9XG5cbiAgcmV0dXJuIHZhbFxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRVSW50QkUgPSBmdW5jdGlvbiByZWFkVUludEJFIChvZmZzZXQsIGJ5dGVMZW5ndGgsIG5vQXNzZXJ0KSB7XG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGggfCAwXG4gIGlmICghbm9Bc3NlcnQpIHtcbiAgICBjaGVja09mZnNldChvZmZzZXQsIGJ5dGVMZW5ndGgsIHRoaXMubGVuZ3RoKVxuICB9XG5cbiAgdmFyIHZhbCA9IHRoaXNbb2Zmc2V0ICsgLS1ieXRlTGVuZ3RoXVxuICB2YXIgbXVsID0gMVxuICB3aGlsZSAoYnl0ZUxlbmd0aCA+IDAgJiYgKG11bCAqPSAweDEwMCkpIHtcbiAgICB2YWwgKz0gdGhpc1tvZmZzZXQgKyAtLWJ5dGVMZW5ndGhdICogbXVsXG4gIH1cblxuICByZXR1cm4gdmFsXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUucmVhZFVJbnQ4ID0gZnVuY3Rpb24gcmVhZFVJbnQ4IChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgMSwgdGhpcy5sZW5ndGgpXG4gIHJldHVybiB0aGlzW29mZnNldF1cbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkVUludDE2TEUgPSBmdW5jdGlvbiByZWFkVUludDE2TEUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCAyLCB0aGlzLmxlbmd0aClcbiAgcmV0dXJuIHRoaXNbb2Zmc2V0XSB8ICh0aGlzW29mZnNldCArIDFdIDw8IDgpXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUucmVhZFVJbnQxNkJFID0gZnVuY3Rpb24gcmVhZFVJbnQxNkJFIChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgMiwgdGhpcy5sZW5ndGgpXG4gIHJldHVybiAodGhpc1tvZmZzZXRdIDw8IDgpIHwgdGhpc1tvZmZzZXQgKyAxXVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRVSW50MzJMRSA9IGZ1bmN0aW9uIHJlYWRVSW50MzJMRSAob2Zmc2V0LCBub0Fzc2VydCkge1xuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIDQsIHRoaXMubGVuZ3RoKVxuXG4gIHJldHVybiAoKHRoaXNbb2Zmc2V0XSkgfFxuICAgICAgKHRoaXNbb2Zmc2V0ICsgMV0gPDwgOCkgfFxuICAgICAgKHRoaXNbb2Zmc2V0ICsgMl0gPDwgMTYpKSArXG4gICAgICAodGhpc1tvZmZzZXQgKyAzXSAqIDB4MTAwMDAwMClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkVUludDMyQkUgPSBmdW5jdGlvbiByZWFkVUludDMyQkUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCA0LCB0aGlzLmxlbmd0aClcblxuICByZXR1cm4gKHRoaXNbb2Zmc2V0XSAqIDB4MTAwMDAwMCkgK1xuICAgICgodGhpc1tvZmZzZXQgKyAxXSA8PCAxNikgfFxuICAgICh0aGlzW29mZnNldCArIDJdIDw8IDgpIHxcbiAgICB0aGlzW29mZnNldCArIDNdKVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRJbnRMRSA9IGZ1bmN0aW9uIHJlYWRJbnRMRSAob2Zmc2V0LCBieXRlTGVuZ3RoLCBub0Fzc2VydCkge1xuICBvZmZzZXQgPSBvZmZzZXQgfCAwXG4gIGJ5dGVMZW5ndGggPSBieXRlTGVuZ3RoIHwgMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIGJ5dGVMZW5ndGgsIHRoaXMubGVuZ3RoKVxuXG4gIHZhciB2YWwgPSB0aGlzW29mZnNldF1cbiAgdmFyIG11bCA9IDFcbiAgdmFyIGkgPSAwXG4gIHdoaWxlICgrK2kgPCBieXRlTGVuZ3RoICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgdmFsICs9IHRoaXNbb2Zmc2V0ICsgaV0gKiBtdWxcbiAgfVxuICBtdWwgKj0gMHg4MFxuXG4gIGlmICh2YWwgPj0gbXVsKSB2YWwgLT0gTWF0aC5wb3coMiwgOCAqIGJ5dGVMZW5ndGgpXG5cbiAgcmV0dXJuIHZhbFxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRJbnRCRSA9IGZ1bmN0aW9uIHJlYWRJbnRCRSAob2Zmc2V0LCBieXRlTGVuZ3RoLCBub0Fzc2VydCkge1xuICBvZmZzZXQgPSBvZmZzZXQgfCAwXG4gIGJ5dGVMZW5ndGggPSBieXRlTGVuZ3RoIHwgMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIGJ5dGVMZW5ndGgsIHRoaXMubGVuZ3RoKVxuXG4gIHZhciBpID0gYnl0ZUxlbmd0aFxuICB2YXIgbXVsID0gMVxuICB2YXIgdmFsID0gdGhpc1tvZmZzZXQgKyAtLWldXG4gIHdoaWxlIChpID4gMCAmJiAobXVsICo9IDB4MTAwKSkge1xuICAgIHZhbCArPSB0aGlzW29mZnNldCArIC0taV0gKiBtdWxcbiAgfVxuICBtdWwgKj0gMHg4MFxuXG4gIGlmICh2YWwgPj0gbXVsKSB2YWwgLT0gTWF0aC5wb3coMiwgOCAqIGJ5dGVMZW5ndGgpXG5cbiAgcmV0dXJuIHZhbFxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRJbnQ4ID0gZnVuY3Rpb24gcmVhZEludDggKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCAxLCB0aGlzLmxlbmd0aClcbiAgaWYgKCEodGhpc1tvZmZzZXRdICYgMHg4MCkpIHJldHVybiAodGhpc1tvZmZzZXRdKVxuICByZXR1cm4gKCgweGZmIC0gdGhpc1tvZmZzZXRdICsgMSkgKiAtMSlcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkSW50MTZMRSA9IGZ1bmN0aW9uIHJlYWRJbnQxNkxFIChvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrT2Zmc2V0KG9mZnNldCwgMiwgdGhpcy5sZW5ndGgpXG4gIHZhciB2YWwgPSB0aGlzW29mZnNldF0gfCAodGhpc1tvZmZzZXQgKyAxXSA8PCA4KVxuICByZXR1cm4gKHZhbCAmIDB4ODAwMCkgPyB2YWwgfCAweEZGRkYwMDAwIDogdmFsXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUucmVhZEludDE2QkUgPSBmdW5jdGlvbiByZWFkSW50MTZCRSAob2Zmc2V0LCBub0Fzc2VydCkge1xuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIDIsIHRoaXMubGVuZ3RoKVxuICB2YXIgdmFsID0gdGhpc1tvZmZzZXQgKyAxXSB8ICh0aGlzW29mZnNldF0gPDwgOClcbiAgcmV0dXJuICh2YWwgJiAweDgwMDApID8gdmFsIHwgMHhGRkZGMDAwMCA6IHZhbFxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRJbnQzMkxFID0gZnVuY3Rpb24gcmVhZEludDMyTEUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCA0LCB0aGlzLmxlbmd0aClcblxuICByZXR1cm4gKHRoaXNbb2Zmc2V0XSkgfFxuICAgICh0aGlzW29mZnNldCArIDFdIDw8IDgpIHxcbiAgICAodGhpc1tvZmZzZXQgKyAyXSA8PCAxNikgfFxuICAgICh0aGlzW29mZnNldCArIDNdIDw8IDI0KVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRJbnQzMkJFID0gZnVuY3Rpb24gcmVhZEludDMyQkUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCA0LCB0aGlzLmxlbmd0aClcblxuICByZXR1cm4gKHRoaXNbb2Zmc2V0XSA8PCAyNCkgfFxuICAgICh0aGlzW29mZnNldCArIDFdIDw8IDE2KSB8XG4gICAgKHRoaXNbb2Zmc2V0ICsgMl0gPDwgOCkgfFxuICAgICh0aGlzW29mZnNldCArIDNdKVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRGbG9hdExFID0gZnVuY3Rpb24gcmVhZEZsb2F0TEUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCA0LCB0aGlzLmxlbmd0aClcbiAgcmV0dXJuIGllZWU3NTQucmVhZCh0aGlzLCBvZmZzZXQsIHRydWUsIDIzLCA0KVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWRGbG9hdEJFID0gZnVuY3Rpb24gcmVhZEZsb2F0QkUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCA0LCB0aGlzLmxlbmd0aClcbiAgcmV0dXJuIGllZWU3NTQucmVhZCh0aGlzLCBvZmZzZXQsIGZhbHNlLCAyMywgNClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS5yZWFkRG91YmxlTEUgPSBmdW5jdGlvbiByZWFkRG91YmxlTEUgKG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tPZmZzZXQob2Zmc2V0LCA4LCB0aGlzLmxlbmd0aClcbiAgcmV0dXJuIGllZWU3NTQucmVhZCh0aGlzLCBvZmZzZXQsIHRydWUsIDUyLCA4KVxufVxuXG5CdWZmZXIucHJvdG90eXBlLnJlYWREb3VibGVCRSA9IGZ1bmN0aW9uIHJlYWREb3VibGVCRSAob2Zmc2V0LCBub0Fzc2VydCkge1xuICBpZiAoIW5vQXNzZXJ0KSBjaGVja09mZnNldChvZmZzZXQsIDgsIHRoaXMubGVuZ3RoKVxuICByZXR1cm4gaWVlZTc1NC5yZWFkKHRoaXMsIG9mZnNldCwgZmFsc2UsIDUyLCA4KVxufVxuXG5mdW5jdGlvbiBjaGVja0ludCAoYnVmLCB2YWx1ZSwgb2Zmc2V0LCBleHQsIG1heCwgbWluKSB7XG4gIGlmICghQnVmZmVyLmlzQnVmZmVyKGJ1ZikpIHRocm93IG5ldyBUeXBlRXJyb3IoJ1wiYnVmZmVyXCIgYXJndW1lbnQgbXVzdCBiZSBhIEJ1ZmZlciBpbnN0YW5jZScpXG4gIGlmICh2YWx1ZSA+IG1heCB8fCB2YWx1ZSA8IG1pbikgdGhyb3cgbmV3IFJhbmdlRXJyb3IoJ1widmFsdWVcIiBhcmd1bWVudCBpcyBvdXQgb2YgYm91bmRzJylcbiAgaWYgKG9mZnNldCArIGV4dCA+IGJ1Zi5sZW5ndGgpIHRocm93IG5ldyBSYW5nZUVycm9yKCdJbmRleCBvdXQgb2YgcmFuZ2UnKVxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlVUludExFID0gZnVuY3Rpb24gd3JpdGVVSW50TEUgKHZhbHVlLCBvZmZzZXQsIGJ5dGVMZW5ndGgsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGggfCAwXG4gIGlmICghbm9Bc3NlcnQpIHtcbiAgICB2YXIgbWF4Qnl0ZXMgPSBNYXRoLnBvdygyLCA4ICogYnl0ZUxlbmd0aCkgLSAxXG4gICAgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgYnl0ZUxlbmd0aCwgbWF4Qnl0ZXMsIDApXG4gIH1cblxuICB2YXIgbXVsID0gMVxuICB2YXIgaSA9IDBcbiAgdGhpc1tvZmZzZXRdID0gdmFsdWUgJiAweEZGXG4gIHdoaWxlICgrK2kgPCBieXRlTGVuZ3RoICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgdGhpc1tvZmZzZXQgKyBpXSA9ICh2YWx1ZSAvIG11bCkgJiAweEZGXG4gIH1cblxuICByZXR1cm4gb2Zmc2V0ICsgYnl0ZUxlbmd0aFxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlVUludEJFID0gZnVuY3Rpb24gd3JpdGVVSW50QkUgKHZhbHVlLCBvZmZzZXQsIGJ5dGVMZW5ndGgsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGggfCAwXG4gIGlmICghbm9Bc3NlcnQpIHtcbiAgICB2YXIgbWF4Qnl0ZXMgPSBNYXRoLnBvdygyLCA4ICogYnl0ZUxlbmd0aCkgLSAxXG4gICAgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgYnl0ZUxlbmd0aCwgbWF4Qnl0ZXMsIDApXG4gIH1cblxuICB2YXIgaSA9IGJ5dGVMZW5ndGggLSAxXG4gIHZhciBtdWwgPSAxXG4gIHRoaXNbb2Zmc2V0ICsgaV0gPSB2YWx1ZSAmIDB4RkZcbiAgd2hpbGUgKC0taSA+PSAwICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgdGhpc1tvZmZzZXQgKyBpXSA9ICh2YWx1ZSAvIG11bCkgJiAweEZGXG4gIH1cblxuICByZXR1cm4gb2Zmc2V0ICsgYnl0ZUxlbmd0aFxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlVUludDggPSBmdW5jdGlvbiB3cml0ZVVJbnQ4ICh2YWx1ZSwgb2Zmc2V0LCBub0Fzc2VydCkge1xuICB2YWx1ZSA9ICt2YWx1ZVxuICBvZmZzZXQgPSBvZmZzZXQgfCAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrSW50KHRoaXMsIHZhbHVlLCBvZmZzZXQsIDEsIDB4ZmYsIDApXG4gIGlmICghQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHZhbHVlID0gTWF0aC5mbG9vcih2YWx1ZSlcbiAgdGhpc1tvZmZzZXRdID0gKHZhbHVlICYgMHhmZilcbiAgcmV0dXJuIG9mZnNldCArIDFcbn1cblxuZnVuY3Rpb24gb2JqZWN0V3JpdGVVSW50MTYgKGJ1ZiwgdmFsdWUsIG9mZnNldCwgbGl0dGxlRW5kaWFuKSB7XG4gIGlmICh2YWx1ZSA8IDApIHZhbHVlID0gMHhmZmZmICsgdmFsdWUgKyAxXG4gIGZvciAodmFyIGkgPSAwLCBqID0gTWF0aC5taW4oYnVmLmxlbmd0aCAtIG9mZnNldCwgMik7IGkgPCBqOyArK2kpIHtcbiAgICBidWZbb2Zmc2V0ICsgaV0gPSAodmFsdWUgJiAoMHhmZiA8PCAoOCAqIChsaXR0bGVFbmRpYW4gPyBpIDogMSAtIGkpKSkpID4+PlxuICAgICAgKGxpdHRsZUVuZGlhbiA/IGkgOiAxIC0gaSkgKiA4XG4gIH1cbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZVVJbnQxNkxFID0gZnVuY3Rpb24gd3JpdGVVSW50MTZMRSAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0IHwgMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja0ludCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCAyLCAweGZmZmYsIDApXG4gIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIHRoaXNbb2Zmc2V0XSA9ICh2YWx1ZSAmIDB4ZmYpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSA+Pj4gOClcbiAgfSBlbHNlIHtcbiAgICBvYmplY3RXcml0ZVVJbnQxNih0aGlzLCB2YWx1ZSwgb2Zmc2V0LCB0cnVlKVxuICB9XG4gIHJldHVybiBvZmZzZXQgKyAyXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVVSW50MTZCRSA9IGZ1bmN0aW9uIHdyaXRlVUludDE2QkUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgMiwgMHhmZmZmLCAwKVxuICBpZiAoQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHtcbiAgICB0aGlzW29mZnNldF0gPSAodmFsdWUgPj4+IDgpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSAmIDB4ZmYpXG4gIH0gZWxzZSB7XG4gICAgb2JqZWN0V3JpdGVVSW50MTYodGhpcywgdmFsdWUsIG9mZnNldCwgZmFsc2UpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDJcbn1cblxuZnVuY3Rpb24gb2JqZWN0V3JpdGVVSW50MzIgKGJ1ZiwgdmFsdWUsIG9mZnNldCwgbGl0dGxlRW5kaWFuKSB7XG4gIGlmICh2YWx1ZSA8IDApIHZhbHVlID0gMHhmZmZmZmZmZiArIHZhbHVlICsgMVxuICBmb3IgKHZhciBpID0gMCwgaiA9IE1hdGgubWluKGJ1Zi5sZW5ndGggLSBvZmZzZXQsIDQpOyBpIDwgajsgKytpKSB7XG4gICAgYnVmW29mZnNldCArIGldID0gKHZhbHVlID4+PiAobGl0dGxlRW5kaWFuID8gaSA6IDMgLSBpKSAqIDgpICYgMHhmZlxuICB9XG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVVSW50MzJMRSA9IGZ1bmN0aW9uIHdyaXRlVUludDMyTEUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgNCwgMHhmZmZmZmZmZiwgMClcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgdGhpc1tvZmZzZXQgKyAzXSA9ICh2YWx1ZSA+Pj4gMjQpXG4gICAgdGhpc1tvZmZzZXQgKyAyXSA9ICh2YWx1ZSA+Pj4gMTYpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSA+Pj4gOClcbiAgICB0aGlzW29mZnNldF0gPSAodmFsdWUgJiAweGZmKVxuICB9IGVsc2Uge1xuICAgIG9iamVjdFdyaXRlVUludDMyKHRoaXMsIHZhbHVlLCBvZmZzZXQsIHRydWUpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDRcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZVVJbnQzMkJFID0gZnVuY3Rpb24gd3JpdGVVSW50MzJCRSAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0IHwgMFxuICBpZiAoIW5vQXNzZXJ0KSBjaGVja0ludCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCA0LCAweGZmZmZmZmZmLCAwKVxuICBpZiAoQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHtcbiAgICB0aGlzW29mZnNldF0gPSAodmFsdWUgPj4+IDI0KVxuICAgIHRoaXNbb2Zmc2V0ICsgMV0gPSAodmFsdWUgPj4+IDE2KVxuICAgIHRoaXNbb2Zmc2V0ICsgMl0gPSAodmFsdWUgPj4+IDgpXG4gICAgdGhpc1tvZmZzZXQgKyAzXSA9ICh2YWx1ZSAmIDB4ZmYpXG4gIH0gZWxzZSB7XG4gICAgb2JqZWN0V3JpdGVVSW50MzIodGhpcywgdmFsdWUsIG9mZnNldCwgZmFsc2UpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDRcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludExFID0gZnVuY3Rpb24gd3JpdGVJbnRMRSAodmFsdWUsIG9mZnNldCwgYnl0ZUxlbmd0aCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0IHwgMFxuICBpZiAoIW5vQXNzZXJ0KSB7XG4gICAgdmFyIGxpbWl0ID0gTWF0aC5wb3coMiwgOCAqIGJ5dGVMZW5ndGggLSAxKVxuXG4gICAgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgYnl0ZUxlbmd0aCwgbGltaXQgLSAxLCAtbGltaXQpXG4gIH1cblxuICB2YXIgaSA9IDBcbiAgdmFyIG11bCA9IDFcbiAgdmFyIHN1YiA9IDBcbiAgdGhpc1tvZmZzZXRdID0gdmFsdWUgJiAweEZGXG4gIHdoaWxlICgrK2kgPCBieXRlTGVuZ3RoICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgaWYgKHZhbHVlIDwgMCAmJiBzdWIgPT09IDAgJiYgdGhpc1tvZmZzZXQgKyBpIC0gMV0gIT09IDApIHtcbiAgICAgIHN1YiA9IDFcbiAgICB9XG4gICAgdGhpc1tvZmZzZXQgKyBpXSA9ICgodmFsdWUgLyBtdWwpID4+IDApIC0gc3ViICYgMHhGRlxuICB9XG5cbiAgcmV0dXJuIG9mZnNldCArIGJ5dGVMZW5ndGhcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludEJFID0gZnVuY3Rpb24gd3JpdGVJbnRCRSAodmFsdWUsIG9mZnNldCwgYnl0ZUxlbmd0aCwgbm9Bc3NlcnQpIHtcbiAgdmFsdWUgPSArdmFsdWVcbiAgb2Zmc2V0ID0gb2Zmc2V0IHwgMFxuICBpZiAoIW5vQXNzZXJ0KSB7XG4gICAgdmFyIGxpbWl0ID0gTWF0aC5wb3coMiwgOCAqIGJ5dGVMZW5ndGggLSAxKVxuXG4gICAgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgYnl0ZUxlbmd0aCwgbGltaXQgLSAxLCAtbGltaXQpXG4gIH1cblxuICB2YXIgaSA9IGJ5dGVMZW5ndGggLSAxXG4gIHZhciBtdWwgPSAxXG4gIHZhciBzdWIgPSAwXG4gIHRoaXNbb2Zmc2V0ICsgaV0gPSB2YWx1ZSAmIDB4RkZcbiAgd2hpbGUgKC0taSA+PSAwICYmIChtdWwgKj0gMHgxMDApKSB7XG4gICAgaWYgKHZhbHVlIDwgMCAmJiBzdWIgPT09IDAgJiYgdGhpc1tvZmZzZXQgKyBpICsgMV0gIT09IDApIHtcbiAgICAgIHN1YiA9IDFcbiAgICB9XG4gICAgdGhpc1tvZmZzZXQgKyBpXSA9ICgodmFsdWUgLyBtdWwpID4+IDApIC0gc3ViICYgMHhGRlxuICB9XG5cbiAgcmV0dXJuIG9mZnNldCArIGJ5dGVMZW5ndGhcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludDggPSBmdW5jdGlvbiB3cml0ZUludDggKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgMSwgMHg3ZiwgLTB4ODApXG4gIGlmICghQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHZhbHVlID0gTWF0aC5mbG9vcih2YWx1ZSlcbiAgaWYgKHZhbHVlIDwgMCkgdmFsdWUgPSAweGZmICsgdmFsdWUgKyAxXG4gIHRoaXNbb2Zmc2V0XSA9ICh2YWx1ZSAmIDB4ZmYpXG4gIHJldHVybiBvZmZzZXQgKyAxXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVJbnQxNkxFID0gZnVuY3Rpb24gd3JpdGVJbnQxNkxFICh2YWx1ZSwgb2Zmc2V0LCBub0Fzc2VydCkge1xuICB2YWx1ZSA9ICt2YWx1ZVxuICBvZmZzZXQgPSBvZmZzZXQgfCAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrSW50KHRoaXMsIHZhbHVlLCBvZmZzZXQsIDIsIDB4N2ZmZiwgLTB4ODAwMClcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgdGhpc1tvZmZzZXRdID0gKHZhbHVlICYgMHhmZilcbiAgICB0aGlzW29mZnNldCArIDFdID0gKHZhbHVlID4+PiA4KVxuICB9IGVsc2Uge1xuICAgIG9iamVjdFdyaXRlVUludDE2KHRoaXMsIHZhbHVlLCBvZmZzZXQsIHRydWUpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDJcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludDE2QkUgPSBmdW5jdGlvbiB3cml0ZUludDE2QkUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgMiwgMHg3ZmZmLCAtMHg4MDAwKVxuICBpZiAoQnVmZmVyLlRZUEVEX0FSUkFZX1NVUFBPUlQpIHtcbiAgICB0aGlzW29mZnNldF0gPSAodmFsdWUgPj4+IDgpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSAmIDB4ZmYpXG4gIH0gZWxzZSB7XG4gICAgb2JqZWN0V3JpdGVVSW50MTYodGhpcywgdmFsdWUsIG9mZnNldCwgZmFsc2UpXG4gIH1cbiAgcmV0dXJuIG9mZnNldCArIDJcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZUludDMyTEUgPSBmdW5jdGlvbiB3cml0ZUludDMyTEUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHZhbHVlID0gK3ZhbHVlXG4gIG9mZnNldCA9IG9mZnNldCB8IDBcbiAgaWYgKCFub0Fzc2VydCkgY2hlY2tJbnQodGhpcywgdmFsdWUsIG9mZnNldCwgNCwgMHg3ZmZmZmZmZiwgLTB4ODAwMDAwMDApXG4gIGlmIChCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIHRoaXNbb2Zmc2V0XSA9ICh2YWx1ZSAmIDB4ZmYpXG4gICAgdGhpc1tvZmZzZXQgKyAxXSA9ICh2YWx1ZSA+Pj4gOClcbiAgICB0aGlzW29mZnNldCArIDJdID0gKHZhbHVlID4+PiAxNilcbiAgICB0aGlzW29mZnNldCArIDNdID0gKHZhbHVlID4+PiAyNClcbiAgfSBlbHNlIHtcbiAgICBvYmplY3RXcml0ZVVJbnQzMih0aGlzLCB2YWx1ZSwgb2Zmc2V0LCB0cnVlKVxuICB9XG4gIHJldHVybiBvZmZzZXQgKyA0XG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVJbnQzMkJFID0gZnVuY3Rpb24gd3JpdGVJbnQzMkJFICh2YWx1ZSwgb2Zmc2V0LCBub0Fzc2VydCkge1xuICB2YWx1ZSA9ICt2YWx1ZVxuICBvZmZzZXQgPSBvZmZzZXQgfCAwXG4gIGlmICghbm9Bc3NlcnQpIGNoZWNrSW50KHRoaXMsIHZhbHVlLCBvZmZzZXQsIDQsIDB4N2ZmZmZmZmYsIC0weDgwMDAwMDAwKVxuICBpZiAodmFsdWUgPCAwKSB2YWx1ZSA9IDB4ZmZmZmZmZmYgKyB2YWx1ZSArIDFcbiAgaWYgKEJ1ZmZlci5UWVBFRF9BUlJBWV9TVVBQT1JUKSB7XG4gICAgdGhpc1tvZmZzZXRdID0gKHZhbHVlID4+PiAyNClcbiAgICB0aGlzW29mZnNldCArIDFdID0gKHZhbHVlID4+PiAxNilcbiAgICB0aGlzW29mZnNldCArIDJdID0gKHZhbHVlID4+PiA4KVxuICAgIHRoaXNbb2Zmc2V0ICsgM10gPSAodmFsdWUgJiAweGZmKVxuICB9IGVsc2Uge1xuICAgIG9iamVjdFdyaXRlVUludDMyKHRoaXMsIHZhbHVlLCBvZmZzZXQsIGZhbHNlKVxuICB9XG4gIHJldHVybiBvZmZzZXQgKyA0XG59XG5cbmZ1bmN0aW9uIGNoZWNrSUVFRTc1NCAoYnVmLCB2YWx1ZSwgb2Zmc2V0LCBleHQsIG1heCwgbWluKSB7XG4gIGlmIChvZmZzZXQgKyBleHQgPiBidWYubGVuZ3RoKSB0aHJvdyBuZXcgUmFuZ2VFcnJvcignSW5kZXggb3V0IG9mIHJhbmdlJylcbiAgaWYgKG9mZnNldCA8IDApIHRocm93IG5ldyBSYW5nZUVycm9yKCdJbmRleCBvdXQgb2YgcmFuZ2UnKVxufVxuXG5mdW5jdGlvbiB3cml0ZUZsb2F0IChidWYsIHZhbHVlLCBvZmZzZXQsIGxpdHRsZUVuZGlhbiwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkge1xuICAgIGNoZWNrSUVFRTc1NChidWYsIHZhbHVlLCBvZmZzZXQsIDQsIDMuNDAyODIzNDY2Mzg1Mjg4NmUrMzgsIC0zLjQwMjgyMzQ2NjM4NTI4ODZlKzM4KVxuICB9XG4gIGllZWU3NTQud3JpdGUoYnVmLCB2YWx1ZSwgb2Zmc2V0LCBsaXR0bGVFbmRpYW4sIDIzLCA0KVxuICByZXR1cm4gb2Zmc2V0ICsgNFxufVxuXG5CdWZmZXIucHJvdG90eXBlLndyaXRlRmxvYXRMRSA9IGZ1bmN0aW9uIHdyaXRlRmxvYXRMRSAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgcmV0dXJuIHdyaXRlRmxvYXQodGhpcywgdmFsdWUsIG9mZnNldCwgdHJ1ZSwgbm9Bc3NlcnQpXG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVGbG9hdEJFID0gZnVuY3Rpb24gd3JpdGVGbG9hdEJFICh2YWx1ZSwgb2Zmc2V0LCBub0Fzc2VydCkge1xuICByZXR1cm4gd3JpdGVGbG9hdCh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCBmYWxzZSwgbm9Bc3NlcnQpXG59XG5cbmZ1bmN0aW9uIHdyaXRlRG91YmxlIChidWYsIHZhbHVlLCBvZmZzZXQsIGxpdHRsZUVuZGlhbiwgbm9Bc3NlcnQpIHtcbiAgaWYgKCFub0Fzc2VydCkge1xuICAgIGNoZWNrSUVFRTc1NChidWYsIHZhbHVlLCBvZmZzZXQsIDgsIDEuNzk3NjkzMTM0ODYyMzE1N0UrMzA4LCAtMS43OTc2OTMxMzQ4NjIzMTU3RSszMDgpXG4gIH1cbiAgaWVlZTc1NC53cml0ZShidWYsIHZhbHVlLCBvZmZzZXQsIGxpdHRsZUVuZGlhbiwgNTIsIDgpXG4gIHJldHVybiBvZmZzZXQgKyA4XG59XG5cbkJ1ZmZlci5wcm90b3R5cGUud3JpdGVEb3VibGVMRSA9IGZ1bmN0aW9uIHdyaXRlRG91YmxlTEUgKHZhbHVlLCBvZmZzZXQsIG5vQXNzZXJ0KSB7XG4gIHJldHVybiB3cml0ZURvdWJsZSh0aGlzLCB2YWx1ZSwgb2Zmc2V0LCB0cnVlLCBub0Fzc2VydClcbn1cblxuQnVmZmVyLnByb3RvdHlwZS53cml0ZURvdWJsZUJFID0gZnVuY3Rpb24gd3JpdGVEb3VibGVCRSAodmFsdWUsIG9mZnNldCwgbm9Bc3NlcnQpIHtcbiAgcmV0dXJuIHdyaXRlRG91YmxlKHRoaXMsIHZhbHVlLCBvZmZzZXQsIGZhbHNlLCBub0Fzc2VydClcbn1cblxuLy8gY29weSh0YXJnZXRCdWZmZXIsIHRhcmdldFN0YXJ0PTAsIHNvdXJjZVN0YXJ0PTAsIHNvdXJjZUVuZD1idWZmZXIubGVuZ3RoKVxuQnVmZmVyLnByb3RvdHlwZS5jb3B5ID0gZnVuY3Rpb24gY29weSAodGFyZ2V0LCB0YXJnZXRTdGFydCwgc3RhcnQsIGVuZCkge1xuICBpZiAoIXN0YXJ0KSBzdGFydCA9IDBcbiAgaWYgKCFlbmQgJiYgZW5kICE9PSAwKSBlbmQgPSB0aGlzLmxlbmd0aFxuICBpZiAodGFyZ2V0U3RhcnQgPj0gdGFyZ2V0Lmxlbmd0aCkgdGFyZ2V0U3RhcnQgPSB0YXJnZXQubGVuZ3RoXG4gIGlmICghdGFyZ2V0U3RhcnQpIHRhcmdldFN0YXJ0ID0gMFxuICBpZiAoZW5kID4gMCAmJiBlbmQgPCBzdGFydCkgZW5kID0gc3RhcnRcblxuICAvLyBDb3B5IDAgYnl0ZXM7IHdlJ3JlIGRvbmVcbiAgaWYgKGVuZCA9PT0gc3RhcnQpIHJldHVybiAwXG4gIGlmICh0YXJnZXQubGVuZ3RoID09PSAwIHx8IHRoaXMubGVuZ3RoID09PSAwKSByZXR1cm4gMFxuXG4gIC8vIEZhdGFsIGVycm9yIGNvbmRpdGlvbnNcbiAgaWYgKHRhcmdldFN0YXJ0IDwgMCkge1xuICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCd0YXJnZXRTdGFydCBvdXQgb2YgYm91bmRzJylcbiAgfVxuICBpZiAoc3RhcnQgPCAwIHx8IHN0YXJ0ID49IHRoaXMubGVuZ3RoKSB0aHJvdyBuZXcgUmFuZ2VFcnJvcignc291cmNlU3RhcnQgb3V0IG9mIGJvdW5kcycpXG4gIGlmIChlbmQgPCAwKSB0aHJvdyBuZXcgUmFuZ2VFcnJvcignc291cmNlRW5kIG91dCBvZiBib3VuZHMnKVxuXG4gIC8vIEFyZSB3ZSBvb2I/XG4gIGlmIChlbmQgPiB0aGlzLmxlbmd0aCkgZW5kID0gdGhpcy5sZW5ndGhcbiAgaWYgKHRhcmdldC5sZW5ndGggLSB0YXJnZXRTdGFydCA8IGVuZCAtIHN0YXJ0KSB7XG4gICAgZW5kID0gdGFyZ2V0Lmxlbmd0aCAtIHRhcmdldFN0YXJ0ICsgc3RhcnRcbiAgfVxuXG4gIHZhciBsZW4gPSBlbmQgLSBzdGFydFxuICB2YXIgaVxuXG4gIGlmICh0aGlzID09PSB0YXJnZXQgJiYgc3RhcnQgPCB0YXJnZXRTdGFydCAmJiB0YXJnZXRTdGFydCA8IGVuZCkge1xuICAgIC8vIGRlc2NlbmRpbmcgY29weSBmcm9tIGVuZFxuICAgIGZvciAoaSA9IGxlbiAtIDE7IGkgPj0gMDsgLS1pKSB7XG4gICAgICB0YXJnZXRbaSArIHRhcmdldFN0YXJ0XSA9IHRoaXNbaSArIHN0YXJ0XVxuICAgIH1cbiAgfSBlbHNlIGlmIChsZW4gPCAxMDAwIHx8ICFCdWZmZXIuVFlQRURfQVJSQVlfU1VQUE9SVCkge1xuICAgIC8vIGFzY2VuZGluZyBjb3B5IGZyb20gc3RhcnRcbiAgICBmb3IgKGkgPSAwOyBpIDwgbGVuOyArK2kpIHtcbiAgICAgIHRhcmdldFtpICsgdGFyZ2V0U3RhcnRdID0gdGhpc1tpICsgc3RhcnRdXG4gICAgfVxuICB9IGVsc2Uge1xuICAgIFVpbnQ4QXJyYXkucHJvdG90eXBlLnNldC5jYWxsKFxuICAgICAgdGFyZ2V0LFxuICAgICAgdGhpcy5zdWJhcnJheShzdGFydCwgc3RhcnQgKyBsZW4pLFxuICAgICAgdGFyZ2V0U3RhcnRcbiAgICApXG4gIH1cblxuICByZXR1cm4gbGVuXG59XG5cbi8vIFVzYWdlOlxuLy8gICAgYnVmZmVyLmZpbGwobnVtYmVyWywgb2Zmc2V0WywgZW5kXV0pXG4vLyAgICBidWZmZXIuZmlsbChidWZmZXJbLCBvZmZzZXRbLCBlbmRdXSlcbi8vICAgIGJ1ZmZlci5maWxsKHN0cmluZ1ssIG9mZnNldFssIGVuZF1dWywgZW5jb2RpbmddKVxuQnVmZmVyLnByb3RvdHlwZS5maWxsID0gZnVuY3Rpb24gZmlsbCAodmFsLCBzdGFydCwgZW5kLCBlbmNvZGluZykge1xuICAvLyBIYW5kbGUgc3RyaW5nIGNhc2VzOlxuICBpZiAodHlwZW9mIHZhbCA9PT0gJ3N0cmluZycpIHtcbiAgICBpZiAodHlwZW9mIHN0YXJ0ID09PSAnc3RyaW5nJykge1xuICAgICAgZW5jb2RpbmcgPSBzdGFydFxuICAgICAgc3RhcnQgPSAwXG4gICAgICBlbmQgPSB0aGlzLmxlbmd0aFxuICAgIH0gZWxzZSBpZiAodHlwZW9mIGVuZCA9PT0gJ3N0cmluZycpIHtcbiAgICAgIGVuY29kaW5nID0gZW5kXG4gICAgICBlbmQgPSB0aGlzLmxlbmd0aFxuICAgIH1cbiAgICBpZiAodmFsLmxlbmd0aCA9PT0gMSkge1xuICAgICAgdmFyIGNvZGUgPSB2YWwuY2hhckNvZGVBdCgwKVxuICAgICAgaWYgKGNvZGUgPCAyNTYpIHtcbiAgICAgICAgdmFsID0gY29kZVxuICAgICAgfVxuICAgIH1cbiAgICBpZiAoZW5jb2RpbmcgIT09IHVuZGVmaW5lZCAmJiB0eXBlb2YgZW5jb2RpbmcgIT09ICdzdHJpbmcnKSB7XG4gICAgICB0aHJvdyBuZXcgVHlwZUVycm9yKCdlbmNvZGluZyBtdXN0IGJlIGEgc3RyaW5nJylcbiAgICB9XG4gICAgaWYgKHR5cGVvZiBlbmNvZGluZyA9PT0gJ3N0cmluZycgJiYgIUJ1ZmZlci5pc0VuY29kaW5nKGVuY29kaW5nKSkge1xuICAgICAgdGhyb3cgbmV3IFR5cGVFcnJvcignVW5rbm93biBlbmNvZGluZzogJyArIGVuY29kaW5nKVxuICAgIH1cbiAgfSBlbHNlIGlmICh0eXBlb2YgdmFsID09PSAnbnVtYmVyJykge1xuICAgIHZhbCA9IHZhbCAmIDI1NVxuICB9XG5cbiAgLy8gSW52YWxpZCByYW5nZXMgYXJlIG5vdCBzZXQgdG8gYSBkZWZhdWx0LCBzbyBjYW4gcmFuZ2UgY2hlY2sgZWFybHkuXG4gIGlmIChzdGFydCA8IDAgfHwgdGhpcy5sZW5ndGggPCBzdGFydCB8fCB0aGlzLmxlbmd0aCA8IGVuZCkge1xuICAgIHRocm93IG5ldyBSYW5nZUVycm9yKCdPdXQgb2YgcmFuZ2UgaW5kZXgnKVxuICB9XG5cbiAgaWYgKGVuZCA8PSBzdGFydCkge1xuICAgIHJldHVybiB0aGlzXG4gIH1cblxuICBzdGFydCA9IHN0YXJ0ID4+PiAwXG4gIGVuZCA9IGVuZCA9PT0gdW5kZWZpbmVkID8gdGhpcy5sZW5ndGggOiBlbmQgPj4+IDBcblxuICBpZiAoIXZhbCkgdmFsID0gMFxuXG4gIHZhciBpXG4gIGlmICh0eXBlb2YgdmFsID09PSAnbnVtYmVyJykge1xuICAgIGZvciAoaSA9IHN0YXJ0OyBpIDwgZW5kOyArK2kpIHtcbiAgICAgIHRoaXNbaV0gPSB2YWxcbiAgICB9XG4gIH0gZWxzZSB7XG4gICAgdmFyIGJ5dGVzID0gQnVmZmVyLmlzQnVmZmVyKHZhbClcbiAgICAgID8gdmFsXG4gICAgICA6IHV0ZjhUb0J5dGVzKG5ldyBCdWZmZXIodmFsLCBlbmNvZGluZykudG9TdHJpbmcoKSlcbiAgICB2YXIgbGVuID0gYnl0ZXMubGVuZ3RoXG4gICAgZm9yIChpID0gMDsgaSA8IGVuZCAtIHN0YXJ0OyArK2kpIHtcbiAgICAgIHRoaXNbaSArIHN0YXJ0XSA9IGJ5dGVzW2kgJSBsZW5dXG4gICAgfVxuICB9XG5cbiAgcmV0dXJuIHRoaXNcbn1cblxuLy8gSEVMUEVSIEZVTkNUSU9OU1xuLy8gPT09PT09PT09PT09PT09PVxuXG52YXIgSU5WQUxJRF9CQVNFNjRfUkUgPSAvW14rXFwvMC05QS1aYS16LV9dL2dcblxuZnVuY3Rpb24gYmFzZTY0Y2xlYW4gKHN0cikge1xuICAvLyBOb2RlIHN0cmlwcyBvdXQgaW52YWxpZCBjaGFyYWN0ZXJzIGxpa2UgXFxuIGFuZCBcXHQgZnJvbSB0aGUgc3RyaW5nLCBiYXNlNjQtanMgZG9lcyBub3RcbiAgc3RyID0gc3RyaW5ndHJpbShzdHIpLnJlcGxhY2UoSU5WQUxJRF9CQVNFNjRfUkUsICcnKVxuICAvLyBOb2RlIGNvbnZlcnRzIHN0cmluZ3Mgd2l0aCBsZW5ndGggPCAyIHRvICcnXG4gIGlmIChzdHIubGVuZ3RoIDwgMikgcmV0dXJuICcnXG4gIC8vIE5vZGUgYWxsb3dzIGZvciBub24tcGFkZGVkIGJhc2U2NCBzdHJpbmdzIChtaXNzaW5nIHRyYWlsaW5nID09PSksIGJhc2U2NC1qcyBkb2VzIG5vdFxuICB3aGlsZSAoc3RyLmxlbmd0aCAlIDQgIT09IDApIHtcbiAgICBzdHIgPSBzdHIgKyAnPSdcbiAgfVxuICByZXR1cm4gc3RyXG59XG5cbmZ1bmN0aW9uIHN0cmluZ3RyaW0gKHN0cikge1xuICBpZiAoc3RyLnRyaW0pIHJldHVybiBzdHIudHJpbSgpXG4gIHJldHVybiBzdHIucmVwbGFjZSgvXlxccyt8XFxzKyQvZywgJycpXG59XG5cbmZ1bmN0aW9uIHRvSGV4IChuKSB7XG4gIGlmIChuIDwgMTYpIHJldHVybiAnMCcgKyBuLnRvU3RyaW5nKDE2KVxuICByZXR1cm4gbi50b1N0cmluZygxNilcbn1cblxuZnVuY3Rpb24gdXRmOFRvQnl0ZXMgKHN0cmluZywgdW5pdHMpIHtcbiAgdW5pdHMgPSB1bml0cyB8fCBJbmZpbml0eVxuICB2YXIgY29kZVBvaW50XG4gIHZhciBsZW5ndGggPSBzdHJpbmcubGVuZ3RoXG4gIHZhciBsZWFkU3Vycm9nYXRlID0gbnVsbFxuICB2YXIgYnl0ZXMgPSBbXVxuXG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGVuZ3RoOyArK2kpIHtcbiAgICBjb2RlUG9pbnQgPSBzdHJpbmcuY2hhckNvZGVBdChpKVxuXG4gICAgLy8gaXMgc3Vycm9nYXRlIGNvbXBvbmVudFxuICAgIGlmIChjb2RlUG9pbnQgPiAweEQ3RkYgJiYgY29kZVBvaW50IDwgMHhFMDAwKSB7XG4gICAgICAvLyBsYXN0IGNoYXIgd2FzIGEgbGVhZFxuICAgICAgaWYgKCFsZWFkU3Vycm9nYXRlKSB7XG4gICAgICAgIC8vIG5vIGxlYWQgeWV0XG4gICAgICAgIGlmIChjb2RlUG9pbnQgPiAweERCRkYpIHtcbiAgICAgICAgICAvLyB1bmV4cGVjdGVkIHRyYWlsXG4gICAgICAgICAgaWYgKCh1bml0cyAtPSAzKSA+IC0xKSBieXRlcy5wdXNoKDB4RUYsIDB4QkYsIDB4QkQpXG4gICAgICAgICAgY29udGludWVcbiAgICAgICAgfSBlbHNlIGlmIChpICsgMSA9PT0gbGVuZ3RoKSB7XG4gICAgICAgICAgLy8gdW5wYWlyZWQgbGVhZFxuICAgICAgICAgIGlmICgodW5pdHMgLT0gMykgPiAtMSkgYnl0ZXMucHVzaCgweEVGLCAweEJGLCAweEJEKVxuICAgICAgICAgIGNvbnRpbnVlXG4gICAgICAgIH1cblxuICAgICAgICAvLyB2YWxpZCBsZWFkXG4gICAgICAgIGxlYWRTdXJyb2dhdGUgPSBjb2RlUG9pbnRcblxuICAgICAgICBjb250aW51ZVxuICAgICAgfVxuXG4gICAgICAvLyAyIGxlYWRzIGluIGEgcm93XG4gICAgICBpZiAoY29kZVBvaW50IDwgMHhEQzAwKSB7XG4gICAgICAgIGlmICgodW5pdHMgLT0gMykgPiAtMSkgYnl0ZXMucHVzaCgweEVGLCAweEJGLCAweEJEKVxuICAgICAgICBsZWFkU3Vycm9nYXRlID0gY29kZVBvaW50XG4gICAgICAgIGNvbnRpbnVlXG4gICAgICB9XG5cbiAgICAgIC8vIHZhbGlkIHN1cnJvZ2F0ZSBwYWlyXG4gICAgICBjb2RlUG9pbnQgPSAobGVhZFN1cnJvZ2F0ZSAtIDB4RDgwMCA8PCAxMCB8IGNvZGVQb2ludCAtIDB4REMwMCkgKyAweDEwMDAwXG4gICAgfSBlbHNlIGlmIChsZWFkU3Vycm9nYXRlKSB7XG4gICAgICAvLyB2YWxpZCBibXAgY2hhciwgYnV0IGxhc3QgY2hhciB3YXMgYSBsZWFkXG4gICAgICBpZiAoKHVuaXRzIC09IDMpID4gLTEpIGJ5dGVzLnB1c2goMHhFRiwgMHhCRiwgMHhCRClcbiAgICB9XG5cbiAgICBsZWFkU3Vycm9nYXRlID0gbnVsbFxuXG4gICAgLy8gZW5jb2RlIHV0ZjhcbiAgICBpZiAoY29kZVBvaW50IDwgMHg4MCkge1xuICAgICAgaWYgKCh1bml0cyAtPSAxKSA8IDApIGJyZWFrXG4gICAgICBieXRlcy5wdXNoKGNvZGVQb2ludClcbiAgICB9IGVsc2UgaWYgKGNvZGVQb2ludCA8IDB4ODAwKSB7XG4gICAgICBpZiAoKHVuaXRzIC09IDIpIDwgMCkgYnJlYWtcbiAgICAgIGJ5dGVzLnB1c2goXG4gICAgICAgIGNvZGVQb2ludCA+PiAweDYgfCAweEMwLFxuICAgICAgICBjb2RlUG9pbnQgJiAweDNGIHwgMHg4MFxuICAgICAgKVxuICAgIH0gZWxzZSBpZiAoY29kZVBvaW50IDwgMHgxMDAwMCkge1xuICAgICAgaWYgKCh1bml0cyAtPSAzKSA8IDApIGJyZWFrXG4gICAgICBieXRlcy5wdXNoKFxuICAgICAgICBjb2RlUG9pbnQgPj4gMHhDIHwgMHhFMCxcbiAgICAgICAgY29kZVBvaW50ID4+IDB4NiAmIDB4M0YgfCAweDgwLFxuICAgICAgICBjb2RlUG9pbnQgJiAweDNGIHwgMHg4MFxuICAgICAgKVxuICAgIH0gZWxzZSBpZiAoY29kZVBvaW50IDwgMHgxMTAwMDApIHtcbiAgICAgIGlmICgodW5pdHMgLT0gNCkgPCAwKSBicmVha1xuICAgICAgYnl0ZXMucHVzaChcbiAgICAgICAgY29kZVBvaW50ID4+IDB4MTIgfCAweEYwLFxuICAgICAgICBjb2RlUG9pbnQgPj4gMHhDICYgMHgzRiB8IDB4ODAsXG4gICAgICAgIGNvZGVQb2ludCA+PiAweDYgJiAweDNGIHwgMHg4MCxcbiAgICAgICAgY29kZVBvaW50ICYgMHgzRiB8IDB4ODBcbiAgICAgIClcbiAgICB9IGVsc2Uge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKCdJbnZhbGlkIGNvZGUgcG9pbnQnKVxuICAgIH1cbiAgfVxuXG4gIHJldHVybiBieXRlc1xufVxuXG5mdW5jdGlvbiBhc2NpaVRvQnl0ZXMgKHN0cikge1xuICB2YXIgYnl0ZUFycmF5ID0gW11cbiAgZm9yICh2YXIgaSA9IDA7IGkgPCBzdHIubGVuZ3RoOyArK2kpIHtcbiAgICAvLyBOb2RlJ3MgY29kZSBzZWVtcyB0byBiZSBkb2luZyB0aGlzIGFuZCBub3QgJiAweDdGLi5cbiAgICBieXRlQXJyYXkucHVzaChzdHIuY2hhckNvZGVBdChpKSAmIDB4RkYpXG4gIH1cbiAgcmV0dXJuIGJ5dGVBcnJheVxufVxuXG5mdW5jdGlvbiB1dGYxNmxlVG9CeXRlcyAoc3RyLCB1bml0cykge1xuICB2YXIgYywgaGksIGxvXG4gIHZhciBieXRlQXJyYXkgPSBbXVxuICBmb3IgKHZhciBpID0gMDsgaSA8IHN0ci5sZW5ndGg7ICsraSkge1xuICAgIGlmICgodW5pdHMgLT0gMikgPCAwKSBicmVha1xuXG4gICAgYyA9IHN0ci5jaGFyQ29kZUF0KGkpXG4gICAgaGkgPSBjID4+IDhcbiAgICBsbyA9IGMgJSAyNTZcbiAgICBieXRlQXJyYXkucHVzaChsbylcbiAgICBieXRlQXJyYXkucHVzaChoaSlcbiAgfVxuXG4gIHJldHVybiBieXRlQXJyYXlcbn1cblxuZnVuY3Rpb24gYmFzZTY0VG9CeXRlcyAoc3RyKSB7XG4gIHJldHVybiBiYXNlNjQudG9CeXRlQXJyYXkoYmFzZTY0Y2xlYW4oc3RyKSlcbn1cblxuZnVuY3Rpb24gYmxpdEJ1ZmZlciAoc3JjLCBkc3QsIG9mZnNldCwgbGVuZ3RoKSB7XG4gIGZvciAodmFyIGkgPSAwOyBpIDwgbGVuZ3RoOyArK2kpIHtcbiAgICBpZiAoKGkgKyBvZmZzZXQgPj0gZHN0Lmxlbmd0aCkgfHwgKGkgPj0gc3JjLmxlbmd0aCkpIGJyZWFrXG4gICAgZHN0W2kgKyBvZmZzZXRdID0gc3JjW2ldXG4gIH1cbiAgcmV0dXJuIGlcbn1cblxuZnVuY3Rpb24gaXNuYW4gKHZhbCkge1xuICByZXR1cm4gdmFsICE9PSB2YWwgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1zZWxmLWNvbXBhcmVcbn1cbiIsInZhciB0b1N0cmluZyA9IHt9LnRvU3RyaW5nO1xuXG5tb2R1bGUuZXhwb3J0cyA9IEFycmF5LmlzQXJyYXkgfHwgZnVuY3Rpb24gKGFycikge1xuICByZXR1cm4gdG9TdHJpbmcuY2FsbChhcnIpID09ICdbb2JqZWN0IEFycmF5XSc7XG59O1xuIiwicmVxdWlyZSgnLi4vLi4vbW9kdWxlcy9jb3JlLnJlZ2V4cC5lc2NhcGUnKTtcbm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZSgnLi4vLi4vbW9kdWxlcy9fY29yZScpLlJlZ0V4cC5lc2NhcGU7XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCkge1xuICBpZiAodHlwZW9mIGl0ICE9ICdmdW5jdGlvbicpIHRocm93IFR5cGVFcnJvcihpdCArICcgaXMgbm90IGEgZnVuY3Rpb24hJyk7XG4gIHJldHVybiBpdDtcbn07XG4iLCJ2YXIgY29mID0gcmVxdWlyZSgnLi9fY29mJyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCwgbXNnKSB7XG4gIGlmICh0eXBlb2YgaXQgIT0gJ251bWJlcicgJiYgY29mKGl0KSAhPSAnTnVtYmVyJykgdGhyb3cgVHlwZUVycm9yKG1zZyk7XG4gIHJldHVybiAraXQ7XG59O1xuIiwiLy8gMjIuMS4zLjMxIEFycmF5LnByb3RvdHlwZVtAQHVuc2NvcGFibGVzXVxudmFyIFVOU0NPUEFCTEVTID0gcmVxdWlyZSgnLi9fd2tzJykoJ3Vuc2NvcGFibGVzJyk7XG52YXIgQXJyYXlQcm90byA9IEFycmF5LnByb3RvdHlwZTtcbmlmIChBcnJheVByb3RvW1VOU0NPUEFCTEVTXSA9PSB1bmRlZmluZWQpIHJlcXVpcmUoJy4vX2hpZGUnKShBcnJheVByb3RvLCBVTlNDT1BBQkxFUywge30pO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoa2V5KSB7XG4gIEFycmF5UHJvdG9bVU5TQ09QQUJMRVNdW2tleV0gPSB0cnVlO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciBhdCA9IHJlcXVpcmUoJy4vX3N0cmluZy1hdCcpKHRydWUpO1xuXG4gLy8gYEFkdmFuY2VTdHJpbmdJbmRleGAgYWJzdHJhY3Qgb3BlcmF0aW9uXG4vLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL2VjbWEyNjIvI3NlYy1hZHZhbmNlc3RyaW5naW5kZXhcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKFMsIGluZGV4LCB1bmljb2RlKSB7XG4gIHJldHVybiBpbmRleCArICh1bmljb2RlID8gYXQoUywgaW5kZXgpLmxlbmd0aCA6IDEpO1xufTtcbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGl0LCBDb25zdHJ1Y3RvciwgbmFtZSwgZm9yYmlkZGVuRmllbGQpIHtcbiAgaWYgKCEoaXQgaW5zdGFuY2VvZiBDb25zdHJ1Y3RvcikgfHwgKGZvcmJpZGRlbkZpZWxkICE9PSB1bmRlZmluZWQgJiYgZm9yYmlkZGVuRmllbGQgaW4gaXQpKSB7XG4gICAgdGhyb3cgVHlwZUVycm9yKG5hbWUgKyAnOiBpbmNvcnJlY3QgaW52b2NhdGlvbiEnKTtcbiAgfSByZXR1cm4gaXQ7XG59O1xuIiwidmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCkge1xuICBpZiAoIWlzT2JqZWN0KGl0KSkgdGhyb3cgVHlwZUVycm9yKGl0ICsgJyBpcyBub3QgYW4gb2JqZWN0IScpO1xuICByZXR1cm4gaXQ7XG59O1xuIiwiLy8gMjIuMS4zLjMgQXJyYXkucHJvdG90eXBlLmNvcHlXaXRoaW4odGFyZ2V0LCBzdGFydCwgZW5kID0gdGhpcy5sZW5ndGgpXG4ndXNlIHN0cmljdCc7XG52YXIgdG9PYmplY3QgPSByZXF1aXJlKCcuL190by1vYmplY3QnKTtcbnZhciB0b0Fic29sdXRlSW5kZXggPSByZXF1aXJlKCcuL190by1hYnNvbHV0ZS1pbmRleCcpO1xudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gW10uY29weVdpdGhpbiB8fCBmdW5jdGlvbiBjb3B5V2l0aGluKHRhcmdldCAvKiA9IDAgKi8sIHN0YXJ0IC8qID0gMCwgZW5kID0gQGxlbmd0aCAqLykge1xuICB2YXIgTyA9IHRvT2JqZWN0KHRoaXMpO1xuICB2YXIgbGVuID0gdG9MZW5ndGgoTy5sZW5ndGgpO1xuICB2YXIgdG8gPSB0b0Fic29sdXRlSW5kZXgodGFyZ2V0LCBsZW4pO1xuICB2YXIgZnJvbSA9IHRvQWJzb2x1dGVJbmRleChzdGFydCwgbGVuKTtcbiAgdmFyIGVuZCA9IGFyZ3VtZW50cy5sZW5ndGggPiAyID8gYXJndW1lbnRzWzJdIDogdW5kZWZpbmVkO1xuICB2YXIgY291bnQgPSBNYXRoLm1pbigoZW5kID09PSB1bmRlZmluZWQgPyBsZW4gOiB0b0Fic29sdXRlSW5kZXgoZW5kLCBsZW4pKSAtIGZyb20sIGxlbiAtIHRvKTtcbiAgdmFyIGluYyA9IDE7XG4gIGlmIChmcm9tIDwgdG8gJiYgdG8gPCBmcm9tICsgY291bnQpIHtcbiAgICBpbmMgPSAtMTtcbiAgICBmcm9tICs9IGNvdW50IC0gMTtcbiAgICB0byArPSBjb3VudCAtIDE7XG4gIH1cbiAgd2hpbGUgKGNvdW50LS0gPiAwKSB7XG4gICAgaWYgKGZyb20gaW4gTykgT1t0b10gPSBPW2Zyb21dO1xuICAgIGVsc2UgZGVsZXRlIE9bdG9dO1xuICAgIHRvICs9IGluYztcbiAgICBmcm9tICs9IGluYztcbiAgfSByZXR1cm4gTztcbn07XG4iLCIvLyAyMi4xLjMuNiBBcnJheS5wcm90b3R5cGUuZmlsbCh2YWx1ZSwgc3RhcnQgPSAwLCBlbmQgPSB0aGlzLmxlbmd0aClcbid1c2Ugc3RyaWN0JztcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIHRvQWJzb2x1dGVJbmRleCA9IHJlcXVpcmUoJy4vX3RvLWFic29sdXRlLWluZGV4Jyk7XG52YXIgdG9MZW5ndGggPSByZXF1aXJlKCcuL190by1sZW5ndGgnKTtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gZmlsbCh2YWx1ZSAvKiAsIHN0YXJ0ID0gMCwgZW5kID0gQGxlbmd0aCAqLykge1xuICB2YXIgTyA9IHRvT2JqZWN0KHRoaXMpO1xuICB2YXIgbGVuZ3RoID0gdG9MZW5ndGgoTy5sZW5ndGgpO1xuICB2YXIgYUxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gIHZhciBpbmRleCA9IHRvQWJzb2x1dGVJbmRleChhTGVuID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCwgbGVuZ3RoKTtcbiAgdmFyIGVuZCA9IGFMZW4gPiAyID8gYXJndW1lbnRzWzJdIDogdW5kZWZpbmVkO1xuICB2YXIgZW5kUG9zID0gZW5kID09PSB1bmRlZmluZWQgPyBsZW5ndGggOiB0b0Fic29sdXRlSW5kZXgoZW5kLCBsZW5ndGgpO1xuICB3aGlsZSAoZW5kUG9zID4gaW5kZXgpIE9baW5kZXgrK10gPSB2YWx1ZTtcbiAgcmV0dXJuIE87XG59O1xuIiwidmFyIGZvck9mID0gcmVxdWlyZSgnLi9fZm9yLW9mJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGl0ZXIsIElURVJBVE9SKSB7XG4gIHZhciByZXN1bHQgPSBbXTtcbiAgZm9yT2YoaXRlciwgZmFsc2UsIHJlc3VsdC5wdXNoLCByZXN1bHQsIElURVJBVE9SKTtcbiAgcmV0dXJuIHJlc3VsdDtcbn07XG4iLCIvLyBmYWxzZSAtPiBBcnJheSNpbmRleE9mXG4vLyB0cnVlICAtPiBBcnJheSNpbmNsdWRlc1xudmFyIHRvSU9iamVjdCA9IHJlcXVpcmUoJy4vX3RvLWlvYmplY3QnKTtcbnZhciB0b0xlbmd0aCA9IHJlcXVpcmUoJy4vX3RvLWxlbmd0aCcpO1xudmFyIHRvQWJzb2x1dGVJbmRleCA9IHJlcXVpcmUoJy4vX3RvLWFic29sdXRlLWluZGV4Jyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChJU19JTkNMVURFUykge1xuICByZXR1cm4gZnVuY3Rpb24gKCR0aGlzLCBlbCwgZnJvbUluZGV4KSB7XG4gICAgdmFyIE8gPSB0b0lPYmplY3QoJHRoaXMpO1xuICAgIHZhciBsZW5ndGggPSB0b0xlbmd0aChPLmxlbmd0aCk7XG4gICAgdmFyIGluZGV4ID0gdG9BYnNvbHV0ZUluZGV4KGZyb21JbmRleCwgbGVuZ3RoKTtcbiAgICB2YXIgdmFsdWU7XG4gICAgLy8gQXJyYXkjaW5jbHVkZXMgdXNlcyBTYW1lVmFsdWVaZXJvIGVxdWFsaXR5IGFsZ29yaXRobVxuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1zZWxmLWNvbXBhcmVcbiAgICBpZiAoSVNfSU5DTFVERVMgJiYgZWwgIT0gZWwpIHdoaWxlIChsZW5ndGggPiBpbmRleCkge1xuICAgICAgdmFsdWUgPSBPW2luZGV4KytdO1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXNlbGYtY29tcGFyZVxuICAgICAgaWYgKHZhbHVlICE9IHZhbHVlKSByZXR1cm4gdHJ1ZTtcbiAgICAvLyBBcnJheSNpbmRleE9mIGlnbm9yZXMgaG9sZXMsIEFycmF5I2luY2x1ZGVzIC0gbm90XG4gICAgfSBlbHNlIGZvciAoO2xlbmd0aCA+IGluZGV4OyBpbmRleCsrKSBpZiAoSVNfSU5DTFVERVMgfHwgaW5kZXggaW4gTykge1xuICAgICAgaWYgKE9baW5kZXhdID09PSBlbCkgcmV0dXJuIElTX0lOQ0xVREVTIHx8IGluZGV4IHx8IDA7XG4gICAgfSByZXR1cm4gIUlTX0lOQ0xVREVTICYmIC0xO1xuICB9O1xufTtcbiIsIi8vIDAgLT4gQXJyYXkjZm9yRWFjaFxuLy8gMSAtPiBBcnJheSNtYXBcbi8vIDIgLT4gQXJyYXkjZmlsdGVyXG4vLyAzIC0+IEFycmF5I3NvbWVcbi8vIDQgLT4gQXJyYXkjZXZlcnlcbi8vIDUgLT4gQXJyYXkjZmluZFxuLy8gNiAtPiBBcnJheSNmaW5kSW5kZXhcbnZhciBjdHggPSByZXF1aXJlKCcuL19jdHgnKTtcbnZhciBJT2JqZWN0ID0gcmVxdWlyZSgnLi9faW9iamVjdCcpO1xudmFyIHRvT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8tb2JqZWN0Jyk7XG52YXIgdG9MZW5ndGggPSByZXF1aXJlKCcuL190by1sZW5ndGgnKTtcbnZhciBhc2MgPSByZXF1aXJlKCcuL19hcnJheS1zcGVjaWVzLWNyZWF0ZScpO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoVFlQRSwgJGNyZWF0ZSkge1xuICB2YXIgSVNfTUFQID0gVFlQRSA9PSAxO1xuICB2YXIgSVNfRklMVEVSID0gVFlQRSA9PSAyO1xuICB2YXIgSVNfU09NRSA9IFRZUEUgPT0gMztcbiAgdmFyIElTX0VWRVJZID0gVFlQRSA9PSA0O1xuICB2YXIgSVNfRklORF9JTkRFWCA9IFRZUEUgPT0gNjtcbiAgdmFyIE5PX0hPTEVTID0gVFlQRSA9PSA1IHx8IElTX0ZJTkRfSU5ERVg7XG4gIHZhciBjcmVhdGUgPSAkY3JlYXRlIHx8IGFzYztcbiAgcmV0dXJuIGZ1bmN0aW9uICgkdGhpcywgY2FsbGJhY2tmbiwgdGhhdCkge1xuICAgIHZhciBPID0gdG9PYmplY3QoJHRoaXMpO1xuICAgIHZhciBzZWxmID0gSU9iamVjdChPKTtcbiAgICB2YXIgZiA9IGN0eChjYWxsYmFja2ZuLCB0aGF0LCAzKTtcbiAgICB2YXIgbGVuZ3RoID0gdG9MZW5ndGgoc2VsZi5sZW5ndGgpO1xuICAgIHZhciBpbmRleCA9IDA7XG4gICAgdmFyIHJlc3VsdCA9IElTX01BUCA/IGNyZWF0ZSgkdGhpcywgbGVuZ3RoKSA6IElTX0ZJTFRFUiA/IGNyZWF0ZSgkdGhpcywgMCkgOiB1bmRlZmluZWQ7XG4gICAgdmFyIHZhbCwgcmVzO1xuICAgIGZvciAoO2xlbmd0aCA+IGluZGV4OyBpbmRleCsrKSBpZiAoTk9fSE9MRVMgfHwgaW5kZXggaW4gc2VsZikge1xuICAgICAgdmFsID0gc2VsZltpbmRleF07XG4gICAgICByZXMgPSBmKHZhbCwgaW5kZXgsIE8pO1xuICAgICAgaWYgKFRZUEUpIHtcbiAgICAgICAgaWYgKElTX01BUCkgcmVzdWx0W2luZGV4XSA9IHJlczsgICAvLyBtYXBcbiAgICAgICAgZWxzZSBpZiAocmVzKSBzd2l0Y2ggKFRZUEUpIHtcbiAgICAgICAgICBjYXNlIDM6IHJldHVybiB0cnVlOyAgICAgICAgICAgICAvLyBzb21lXG4gICAgICAgICAgY2FzZSA1OiByZXR1cm4gdmFsOyAgICAgICAgICAgICAgLy8gZmluZFxuICAgICAgICAgIGNhc2UgNjogcmV0dXJuIGluZGV4OyAgICAgICAgICAgIC8vIGZpbmRJbmRleFxuICAgICAgICAgIGNhc2UgMjogcmVzdWx0LnB1c2godmFsKTsgICAgICAgIC8vIGZpbHRlclxuICAgICAgICB9IGVsc2UgaWYgKElTX0VWRVJZKSByZXR1cm4gZmFsc2U7IC8vIGV2ZXJ5XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiBJU19GSU5EX0lOREVYID8gLTEgOiBJU19TT01FIHx8IElTX0VWRVJZID8gSVNfRVZFUlkgOiByZXN1bHQ7XG4gIH07XG59O1xuIiwidmFyIGFGdW5jdGlvbiA9IHJlcXVpcmUoJy4vX2EtZnVuY3Rpb24nKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIElPYmplY3QgPSByZXF1aXJlKCcuL19pb2JqZWN0Jyk7XG52YXIgdG9MZW5ndGggPSByZXF1aXJlKCcuL190by1sZW5ndGgnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAodGhhdCwgY2FsbGJhY2tmbiwgYUxlbiwgbWVtbywgaXNSaWdodCkge1xuICBhRnVuY3Rpb24oY2FsbGJhY2tmbik7XG4gIHZhciBPID0gdG9PYmplY3QodGhhdCk7XG4gIHZhciBzZWxmID0gSU9iamVjdChPKTtcbiAgdmFyIGxlbmd0aCA9IHRvTGVuZ3RoKE8ubGVuZ3RoKTtcbiAgdmFyIGluZGV4ID0gaXNSaWdodCA/IGxlbmd0aCAtIDEgOiAwO1xuICB2YXIgaSA9IGlzUmlnaHQgPyAtMSA6IDE7XG4gIGlmIChhTGVuIDwgMikgZm9yICg7Oykge1xuICAgIGlmIChpbmRleCBpbiBzZWxmKSB7XG4gICAgICBtZW1vID0gc2VsZltpbmRleF07XG4gICAgICBpbmRleCArPSBpO1xuICAgICAgYnJlYWs7XG4gICAgfVxuICAgIGluZGV4ICs9IGk7XG4gICAgaWYgKGlzUmlnaHQgPyBpbmRleCA8IDAgOiBsZW5ndGggPD0gaW5kZXgpIHtcbiAgICAgIHRocm93IFR5cGVFcnJvcignUmVkdWNlIG9mIGVtcHR5IGFycmF5IHdpdGggbm8gaW5pdGlhbCB2YWx1ZScpO1xuICAgIH1cbiAgfVxuICBmb3IgKDtpc1JpZ2h0ID8gaW5kZXggPj0gMCA6IGxlbmd0aCA+IGluZGV4OyBpbmRleCArPSBpKSBpZiAoaW5kZXggaW4gc2VsZikge1xuICAgIG1lbW8gPSBjYWxsYmFja2ZuKG1lbW8sIHNlbGZbaW5kZXhdLCBpbmRleCwgTyk7XG4gIH1cbiAgcmV0dXJuIG1lbW87XG59O1xuIiwidmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG52YXIgaXNBcnJheSA9IHJlcXVpcmUoJy4vX2lzLWFycmF5Jyk7XG52YXIgU1BFQ0lFUyA9IHJlcXVpcmUoJy4vX3drcycpKCdzcGVjaWVzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKG9yaWdpbmFsKSB7XG4gIHZhciBDO1xuICBpZiAoaXNBcnJheShvcmlnaW5hbCkpIHtcbiAgICBDID0gb3JpZ2luYWwuY29uc3RydWN0b3I7XG4gICAgLy8gY3Jvc3MtcmVhbG0gZmFsbGJhY2tcbiAgICBpZiAodHlwZW9mIEMgPT0gJ2Z1bmN0aW9uJyAmJiAoQyA9PT0gQXJyYXkgfHwgaXNBcnJheShDLnByb3RvdHlwZSkpKSBDID0gdW5kZWZpbmVkO1xuICAgIGlmIChpc09iamVjdChDKSkge1xuICAgICAgQyA9IENbU1BFQ0lFU107XG4gICAgICBpZiAoQyA9PT0gbnVsbCkgQyA9IHVuZGVmaW5lZDtcbiAgICB9XG4gIH0gcmV0dXJuIEMgPT09IHVuZGVmaW5lZCA/IEFycmF5IDogQztcbn07XG4iLCIvLyA5LjQuMi4zIEFycmF5U3BlY2llc0NyZWF0ZShvcmlnaW5hbEFycmF5LCBsZW5ndGgpXG52YXIgc3BlY2llc0NvbnN0cnVjdG9yID0gcmVxdWlyZSgnLi9fYXJyYXktc3BlY2llcy1jb25zdHJ1Y3RvcicpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChvcmlnaW5hbCwgbGVuZ3RoKSB7XG4gIHJldHVybiBuZXcgKHNwZWNpZXNDb25zdHJ1Y3RvcihvcmlnaW5hbCkpKGxlbmd0aCk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIGFGdW5jdGlvbiA9IHJlcXVpcmUoJy4vX2EtZnVuY3Rpb24nKTtcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xudmFyIGludm9rZSA9IHJlcXVpcmUoJy4vX2ludm9rZScpO1xudmFyIGFycmF5U2xpY2UgPSBbXS5zbGljZTtcbnZhciBmYWN0b3JpZXMgPSB7fTtcblxudmFyIGNvbnN0cnVjdCA9IGZ1bmN0aW9uIChGLCBsZW4sIGFyZ3MpIHtcbiAgaWYgKCEobGVuIGluIGZhY3RvcmllcykpIHtcbiAgICBmb3IgKHZhciBuID0gW10sIGkgPSAwOyBpIDwgbGVuOyBpKyspIG5baV0gPSAnYVsnICsgaSArICddJztcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tbmV3LWZ1bmNcbiAgICBmYWN0b3JpZXNbbGVuXSA9IEZ1bmN0aW9uKCdGLGEnLCAncmV0dXJuIG5ldyBGKCcgKyBuLmpvaW4oJywnKSArICcpJyk7XG4gIH0gcmV0dXJuIGZhY3Rvcmllc1tsZW5dKEYsIGFyZ3MpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBGdW5jdGlvbi5iaW5kIHx8IGZ1bmN0aW9uIGJpbmQodGhhdCAvKiAsIC4uLmFyZ3MgKi8pIHtcbiAgdmFyIGZuID0gYUZ1bmN0aW9uKHRoaXMpO1xuICB2YXIgcGFydEFyZ3MgPSBhcnJheVNsaWNlLmNhbGwoYXJndW1lbnRzLCAxKTtcbiAgdmFyIGJvdW5kID0gZnVuY3Rpb24gKC8qIGFyZ3MuLi4gKi8pIHtcbiAgICB2YXIgYXJncyA9IHBhcnRBcmdzLmNvbmNhdChhcnJheVNsaWNlLmNhbGwoYXJndW1lbnRzKSk7XG4gICAgcmV0dXJuIHRoaXMgaW5zdGFuY2VvZiBib3VuZCA/IGNvbnN0cnVjdChmbiwgYXJncy5sZW5ndGgsIGFyZ3MpIDogaW52b2tlKGZuLCBhcmdzLCB0aGF0KTtcbiAgfTtcbiAgaWYgKGlzT2JqZWN0KGZuLnByb3RvdHlwZSkpIGJvdW5kLnByb3RvdHlwZSA9IGZuLnByb3RvdHlwZTtcbiAgcmV0dXJuIGJvdW5kO1xufTtcbiIsIi8vIGdldHRpbmcgdGFnIGZyb20gMTkuMS4zLjYgT2JqZWN0LnByb3RvdHlwZS50b1N0cmluZygpXG52YXIgY29mID0gcmVxdWlyZSgnLi9fY29mJyk7XG52YXIgVEFHID0gcmVxdWlyZSgnLi9fd2tzJykoJ3RvU3RyaW5nVGFnJyk7XG4vLyBFUzMgd3JvbmcgaGVyZVxudmFyIEFSRyA9IGNvZihmdW5jdGlvbiAoKSB7IHJldHVybiBhcmd1bWVudHM7IH0oKSkgPT0gJ0FyZ3VtZW50cyc7XG5cbi8vIGZhbGxiYWNrIGZvciBJRTExIFNjcmlwdCBBY2Nlc3MgRGVuaWVkIGVycm9yXG52YXIgdHJ5R2V0ID0gZnVuY3Rpb24gKGl0LCBrZXkpIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gaXRba2V5XTtcbiAgfSBjYXRjaCAoZSkgeyAvKiBlbXB0eSAqLyB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCkge1xuICB2YXIgTywgVCwgQjtcbiAgcmV0dXJuIGl0ID09PSB1bmRlZmluZWQgPyAnVW5kZWZpbmVkJyA6IGl0ID09PSBudWxsID8gJ051bGwnXG4gICAgLy8gQEB0b1N0cmluZ1RhZyBjYXNlXG4gICAgOiB0eXBlb2YgKFQgPSB0cnlHZXQoTyA9IE9iamVjdChpdCksIFRBRykpID09ICdzdHJpbmcnID8gVFxuICAgIC8vIGJ1aWx0aW5UYWcgY2FzZVxuICAgIDogQVJHID8gY29mKE8pXG4gICAgLy8gRVMzIGFyZ3VtZW50cyBmYWxsYmFja1xuICAgIDogKEIgPSBjb2YoTykpID09ICdPYmplY3QnICYmIHR5cGVvZiBPLmNhbGxlZSA9PSAnZnVuY3Rpb24nID8gJ0FyZ3VtZW50cycgOiBCO1xufTtcbiIsInZhciB0b1N0cmluZyA9IHt9LnRvU3RyaW5nO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCkge1xuICByZXR1cm4gdG9TdHJpbmcuY2FsbChpdCkuc2xpY2UoOCwgLTEpO1xufTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciBkUCA9IHJlcXVpcmUoJy4vX29iamVjdC1kcCcpLmY7XG52YXIgY3JlYXRlID0gcmVxdWlyZSgnLi9fb2JqZWN0LWNyZWF0ZScpO1xudmFyIHJlZGVmaW5lQWxsID0gcmVxdWlyZSgnLi9fcmVkZWZpbmUtYWxsJyk7XG52YXIgY3R4ID0gcmVxdWlyZSgnLi9fY3R4Jyk7XG52YXIgYW5JbnN0YW5jZSA9IHJlcXVpcmUoJy4vX2FuLWluc3RhbmNlJyk7XG52YXIgZm9yT2YgPSByZXF1aXJlKCcuL19mb3Itb2YnKTtcbnZhciAkaXRlckRlZmluZSA9IHJlcXVpcmUoJy4vX2l0ZXItZGVmaW5lJyk7XG52YXIgc3RlcCA9IHJlcXVpcmUoJy4vX2l0ZXItc3RlcCcpO1xudmFyIHNldFNwZWNpZXMgPSByZXF1aXJlKCcuL19zZXQtc3BlY2llcycpO1xudmFyIERFU0NSSVBUT1JTID0gcmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKTtcbnZhciBmYXN0S2V5ID0gcmVxdWlyZSgnLi9fbWV0YScpLmZhc3RLZXk7XG52YXIgdmFsaWRhdGUgPSByZXF1aXJlKCcuL192YWxpZGF0ZS1jb2xsZWN0aW9uJyk7XG52YXIgU0laRSA9IERFU0NSSVBUT1JTID8gJ19zJyA6ICdzaXplJztcblxudmFyIGdldEVudHJ5ID0gZnVuY3Rpb24gKHRoYXQsIGtleSkge1xuICAvLyBmYXN0IGNhc2VcbiAgdmFyIGluZGV4ID0gZmFzdEtleShrZXkpO1xuICB2YXIgZW50cnk7XG4gIGlmIChpbmRleCAhPT0gJ0YnKSByZXR1cm4gdGhhdC5faVtpbmRleF07XG4gIC8vIGZyb3plbiBvYmplY3QgY2FzZVxuICBmb3IgKGVudHJ5ID0gdGhhdC5fZjsgZW50cnk7IGVudHJ5ID0gZW50cnkubikge1xuICAgIGlmIChlbnRyeS5rID09IGtleSkgcmV0dXJuIGVudHJ5O1xuICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgZ2V0Q29uc3RydWN0b3I6IGZ1bmN0aW9uICh3cmFwcGVyLCBOQU1FLCBJU19NQVAsIEFEREVSKSB7XG4gICAgdmFyIEMgPSB3cmFwcGVyKGZ1bmN0aW9uICh0aGF0LCBpdGVyYWJsZSkge1xuICAgICAgYW5JbnN0YW5jZSh0aGF0LCBDLCBOQU1FLCAnX2knKTtcbiAgICAgIHRoYXQuX3QgPSBOQU1FOyAgICAgICAgIC8vIGNvbGxlY3Rpb24gdHlwZVxuICAgICAgdGhhdC5faSA9IGNyZWF0ZShudWxsKTsgLy8gaW5kZXhcbiAgICAgIHRoYXQuX2YgPSB1bmRlZmluZWQ7ICAgIC8vIGZpcnN0IGVudHJ5XG4gICAgICB0aGF0Ll9sID0gdW5kZWZpbmVkOyAgICAvLyBsYXN0IGVudHJ5XG4gICAgICB0aGF0W1NJWkVdID0gMDsgICAgICAgICAvLyBzaXplXG4gICAgICBpZiAoaXRlcmFibGUgIT0gdW5kZWZpbmVkKSBmb3JPZihpdGVyYWJsZSwgSVNfTUFQLCB0aGF0W0FEREVSXSwgdGhhdCk7XG4gICAgfSk7XG4gICAgcmVkZWZpbmVBbGwoQy5wcm90b3R5cGUsIHtcbiAgICAgIC8vIDIzLjEuMy4xIE1hcC5wcm90b3R5cGUuY2xlYXIoKVxuICAgICAgLy8gMjMuMi4zLjIgU2V0LnByb3RvdHlwZS5jbGVhcigpXG4gICAgICBjbGVhcjogZnVuY3Rpb24gY2xlYXIoKSB7XG4gICAgICAgIGZvciAodmFyIHRoYXQgPSB2YWxpZGF0ZSh0aGlzLCBOQU1FKSwgZGF0YSA9IHRoYXQuX2ksIGVudHJ5ID0gdGhhdC5fZjsgZW50cnk7IGVudHJ5ID0gZW50cnkubikge1xuICAgICAgICAgIGVudHJ5LnIgPSB0cnVlO1xuICAgICAgICAgIGlmIChlbnRyeS5wKSBlbnRyeS5wID0gZW50cnkucC5uID0gdW5kZWZpbmVkO1xuICAgICAgICAgIGRlbGV0ZSBkYXRhW2VudHJ5LmldO1xuICAgICAgICB9XG4gICAgICAgIHRoYXQuX2YgPSB0aGF0Ll9sID0gdW5kZWZpbmVkO1xuICAgICAgICB0aGF0W1NJWkVdID0gMDtcbiAgICAgIH0sXG4gICAgICAvLyAyMy4xLjMuMyBNYXAucHJvdG90eXBlLmRlbGV0ZShrZXkpXG4gICAgICAvLyAyMy4yLjMuNCBTZXQucHJvdG90eXBlLmRlbGV0ZSh2YWx1ZSlcbiAgICAgICdkZWxldGUnOiBmdW5jdGlvbiAoa2V5KSB7XG4gICAgICAgIHZhciB0aGF0ID0gdmFsaWRhdGUodGhpcywgTkFNRSk7XG4gICAgICAgIHZhciBlbnRyeSA9IGdldEVudHJ5KHRoYXQsIGtleSk7XG4gICAgICAgIGlmIChlbnRyeSkge1xuICAgICAgICAgIHZhciBuZXh0ID0gZW50cnkubjtcbiAgICAgICAgICB2YXIgcHJldiA9IGVudHJ5LnA7XG4gICAgICAgICAgZGVsZXRlIHRoYXQuX2lbZW50cnkuaV07XG4gICAgICAgICAgZW50cnkuciA9IHRydWU7XG4gICAgICAgICAgaWYgKHByZXYpIHByZXYubiA9IG5leHQ7XG4gICAgICAgICAgaWYgKG5leHQpIG5leHQucCA9IHByZXY7XG4gICAgICAgICAgaWYgKHRoYXQuX2YgPT0gZW50cnkpIHRoYXQuX2YgPSBuZXh0O1xuICAgICAgICAgIGlmICh0aGF0Ll9sID09IGVudHJ5KSB0aGF0Ll9sID0gcHJldjtcbiAgICAgICAgICB0aGF0W1NJWkVdLS07XG4gICAgICAgIH0gcmV0dXJuICEhZW50cnk7XG4gICAgICB9LFxuICAgICAgLy8gMjMuMi4zLjYgU2V0LnByb3RvdHlwZS5mb3JFYWNoKGNhbGxiYWNrZm4sIHRoaXNBcmcgPSB1bmRlZmluZWQpXG4gICAgICAvLyAyMy4xLjMuNSBNYXAucHJvdG90eXBlLmZvckVhY2goY2FsbGJhY2tmbiwgdGhpc0FyZyA9IHVuZGVmaW5lZClcbiAgICAgIGZvckVhY2g6IGZ1bmN0aW9uIGZvckVhY2goY2FsbGJhY2tmbiAvKiAsIHRoYXQgPSB1bmRlZmluZWQgKi8pIHtcbiAgICAgICAgdmFsaWRhdGUodGhpcywgTkFNRSk7XG4gICAgICAgIHZhciBmID0gY3R4KGNhbGxiYWNrZm4sIGFyZ3VtZW50cy5sZW5ndGggPiAxID8gYXJndW1lbnRzWzFdIDogdW5kZWZpbmVkLCAzKTtcbiAgICAgICAgdmFyIGVudHJ5O1xuICAgICAgICB3aGlsZSAoZW50cnkgPSBlbnRyeSA/IGVudHJ5Lm4gOiB0aGlzLl9mKSB7XG4gICAgICAgICAgZihlbnRyeS52LCBlbnRyeS5rLCB0aGlzKTtcbiAgICAgICAgICAvLyByZXZlcnQgdG8gdGhlIGxhc3QgZXhpc3RpbmcgZW50cnlcbiAgICAgICAgICB3aGlsZSAoZW50cnkgJiYgZW50cnkucikgZW50cnkgPSBlbnRyeS5wO1xuICAgICAgICB9XG4gICAgICB9LFxuICAgICAgLy8gMjMuMS4zLjcgTWFwLnByb3RvdHlwZS5oYXMoa2V5KVxuICAgICAgLy8gMjMuMi4zLjcgU2V0LnByb3RvdHlwZS5oYXModmFsdWUpXG4gICAgICBoYXM6IGZ1bmN0aW9uIGhhcyhrZXkpIHtcbiAgICAgICAgcmV0dXJuICEhZ2V0RW50cnkodmFsaWRhdGUodGhpcywgTkFNRSksIGtleSk7XG4gICAgICB9XG4gICAgfSk7XG4gICAgaWYgKERFU0NSSVBUT1JTKSBkUChDLnByb3RvdHlwZSwgJ3NpemUnLCB7XG4gICAgICBnZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgcmV0dXJuIHZhbGlkYXRlKHRoaXMsIE5BTUUpW1NJWkVdO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiBDO1xuICB9LFxuICBkZWY6IGZ1bmN0aW9uICh0aGF0LCBrZXksIHZhbHVlKSB7XG4gICAgdmFyIGVudHJ5ID0gZ2V0RW50cnkodGhhdCwga2V5KTtcbiAgICB2YXIgcHJldiwgaW5kZXg7XG4gICAgLy8gY2hhbmdlIGV4aXN0aW5nIGVudHJ5XG4gICAgaWYgKGVudHJ5KSB7XG4gICAgICBlbnRyeS52ID0gdmFsdWU7XG4gICAgLy8gY3JlYXRlIG5ldyBlbnRyeVxuICAgIH0gZWxzZSB7XG4gICAgICB0aGF0Ll9sID0gZW50cnkgPSB7XG4gICAgICAgIGk6IGluZGV4ID0gZmFzdEtleShrZXksIHRydWUpLCAvLyA8LSBpbmRleFxuICAgICAgICBrOiBrZXksICAgICAgICAgICAgICAgICAgICAgICAgLy8gPC0ga2V5XG4gICAgICAgIHY6IHZhbHVlLCAgICAgICAgICAgICAgICAgICAgICAvLyA8LSB2YWx1ZVxuICAgICAgICBwOiBwcmV2ID0gdGhhdC5fbCwgICAgICAgICAgICAgLy8gPC0gcHJldmlvdXMgZW50cnlcbiAgICAgICAgbjogdW5kZWZpbmVkLCAgICAgICAgICAgICAgICAgIC8vIDwtIG5leHQgZW50cnlcbiAgICAgICAgcjogZmFsc2UgICAgICAgICAgICAgICAgICAgICAgIC8vIDwtIHJlbW92ZWRcbiAgICAgIH07XG4gICAgICBpZiAoIXRoYXQuX2YpIHRoYXQuX2YgPSBlbnRyeTtcbiAgICAgIGlmIChwcmV2KSBwcmV2Lm4gPSBlbnRyeTtcbiAgICAgIHRoYXRbU0laRV0rKztcbiAgICAgIC8vIGFkZCB0byBpbmRleFxuICAgICAgaWYgKGluZGV4ICE9PSAnRicpIHRoYXQuX2lbaW5kZXhdID0gZW50cnk7XG4gICAgfSByZXR1cm4gdGhhdDtcbiAgfSxcbiAgZ2V0RW50cnk6IGdldEVudHJ5LFxuICBzZXRTdHJvbmc6IGZ1bmN0aW9uIChDLCBOQU1FLCBJU19NQVApIHtcbiAgICAvLyBhZGQgLmtleXMsIC52YWx1ZXMsIC5lbnRyaWVzLCBbQEBpdGVyYXRvcl1cbiAgICAvLyAyMy4xLjMuNCwgMjMuMS4zLjgsIDIzLjEuMy4xMSwgMjMuMS4zLjEyLCAyMy4yLjMuNSwgMjMuMi4zLjgsIDIzLjIuMy4xMCwgMjMuMi4zLjExXG4gICAgJGl0ZXJEZWZpbmUoQywgTkFNRSwgZnVuY3Rpb24gKGl0ZXJhdGVkLCBraW5kKSB7XG4gICAgICB0aGlzLl90ID0gdmFsaWRhdGUoaXRlcmF0ZWQsIE5BTUUpOyAvLyB0YXJnZXRcbiAgICAgIHRoaXMuX2sgPSBraW5kOyAgICAgICAgICAgICAgICAgICAgIC8vIGtpbmRcbiAgICAgIHRoaXMuX2wgPSB1bmRlZmluZWQ7ICAgICAgICAgICAgICAgIC8vIHByZXZpb3VzXG4gICAgfSwgZnVuY3Rpb24gKCkge1xuICAgICAgdmFyIHRoYXQgPSB0aGlzO1xuICAgICAgdmFyIGtpbmQgPSB0aGF0Ll9rO1xuICAgICAgdmFyIGVudHJ5ID0gdGhhdC5fbDtcbiAgICAgIC8vIHJldmVydCB0byB0aGUgbGFzdCBleGlzdGluZyBlbnRyeVxuICAgICAgd2hpbGUgKGVudHJ5ICYmIGVudHJ5LnIpIGVudHJ5ID0gZW50cnkucDtcbiAgICAgIC8vIGdldCBuZXh0IGVudHJ5XG4gICAgICBpZiAoIXRoYXQuX3QgfHwgISh0aGF0Ll9sID0gZW50cnkgPSBlbnRyeSA/IGVudHJ5Lm4gOiB0aGF0Ll90Ll9mKSkge1xuICAgICAgICAvLyBvciBmaW5pc2ggdGhlIGl0ZXJhdGlvblxuICAgICAgICB0aGF0Ll90ID0gdW5kZWZpbmVkO1xuICAgICAgICByZXR1cm4gc3RlcCgxKTtcbiAgICAgIH1cbiAgICAgIC8vIHJldHVybiBzdGVwIGJ5IGtpbmRcbiAgICAgIGlmIChraW5kID09ICdrZXlzJykgcmV0dXJuIHN0ZXAoMCwgZW50cnkuayk7XG4gICAgICBpZiAoa2luZCA9PSAndmFsdWVzJykgcmV0dXJuIHN0ZXAoMCwgZW50cnkudik7XG4gICAgICByZXR1cm4gc3RlcCgwLCBbZW50cnkuaywgZW50cnkudl0pO1xuICAgIH0sIElTX01BUCA/ICdlbnRyaWVzJyA6ICd2YWx1ZXMnLCAhSVNfTUFQLCB0cnVlKTtcblxuICAgIC8vIGFkZCBbQEBzcGVjaWVzXSwgMjMuMS4yLjIsIDIzLjIuMi4yXG4gICAgc2V0U3BlY2llcyhOQU1FKTtcbiAgfVxufTtcbiIsIi8vIGh0dHBzOi8vZ2l0aHViLmNvbS9EYXZpZEJydWFudC9NYXAtU2V0LnByb3RvdHlwZS50b0pTT05cbnZhciBjbGFzc29mID0gcmVxdWlyZSgnLi9fY2xhc3NvZicpO1xudmFyIGZyb20gPSByZXF1aXJlKCcuL19hcnJheS1mcm9tLWl0ZXJhYmxlJyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChOQU1FKSB7XG4gIHJldHVybiBmdW5jdGlvbiB0b0pTT04oKSB7XG4gICAgaWYgKGNsYXNzb2YodGhpcykgIT0gTkFNRSkgdGhyb3cgVHlwZUVycm9yKE5BTUUgKyBcIiN0b0pTT04gaXNuJ3QgZ2VuZXJpY1wiKTtcbiAgICByZXR1cm4gZnJvbSh0aGlzKTtcbiAgfTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG52YXIgcmVkZWZpbmVBbGwgPSByZXF1aXJlKCcuL19yZWRlZmluZS1hbGwnKTtcbnZhciBnZXRXZWFrID0gcmVxdWlyZSgnLi9fbWV0YScpLmdldFdlYWs7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xudmFyIGFuSW5zdGFuY2UgPSByZXF1aXJlKCcuL19hbi1pbnN0YW5jZScpO1xudmFyIGZvck9mID0gcmVxdWlyZSgnLi9fZm9yLW9mJyk7XG52YXIgY3JlYXRlQXJyYXlNZXRob2QgPSByZXF1aXJlKCcuL19hcnJheS1tZXRob2RzJyk7XG52YXIgJGhhcyA9IHJlcXVpcmUoJy4vX2hhcycpO1xudmFyIHZhbGlkYXRlID0gcmVxdWlyZSgnLi9fdmFsaWRhdGUtY29sbGVjdGlvbicpO1xudmFyIGFycmF5RmluZCA9IGNyZWF0ZUFycmF5TWV0aG9kKDUpO1xudmFyIGFycmF5RmluZEluZGV4ID0gY3JlYXRlQXJyYXlNZXRob2QoNik7XG52YXIgaWQgPSAwO1xuXG4vLyBmYWxsYmFjayBmb3IgdW5jYXVnaHQgZnJvemVuIGtleXNcbnZhciB1bmNhdWdodEZyb3plblN0b3JlID0gZnVuY3Rpb24gKHRoYXQpIHtcbiAgcmV0dXJuIHRoYXQuX2wgfHwgKHRoYXQuX2wgPSBuZXcgVW5jYXVnaHRGcm96ZW5TdG9yZSgpKTtcbn07XG52YXIgVW5jYXVnaHRGcm96ZW5TdG9yZSA9IGZ1bmN0aW9uICgpIHtcbiAgdGhpcy5hID0gW107XG59O1xudmFyIGZpbmRVbmNhdWdodEZyb3plbiA9IGZ1bmN0aW9uIChzdG9yZSwga2V5KSB7XG4gIHJldHVybiBhcnJheUZpbmQoc3RvcmUuYSwgZnVuY3Rpb24gKGl0KSB7XG4gICAgcmV0dXJuIGl0WzBdID09PSBrZXk7XG4gIH0pO1xufTtcblVuY2F1Z2h0RnJvemVuU3RvcmUucHJvdG90eXBlID0ge1xuICBnZXQ6IGZ1bmN0aW9uIChrZXkpIHtcbiAgICB2YXIgZW50cnkgPSBmaW5kVW5jYXVnaHRGcm96ZW4odGhpcywga2V5KTtcbiAgICBpZiAoZW50cnkpIHJldHVybiBlbnRyeVsxXTtcbiAgfSxcbiAgaGFzOiBmdW5jdGlvbiAoa2V5KSB7XG4gICAgcmV0dXJuICEhZmluZFVuY2F1Z2h0RnJvemVuKHRoaXMsIGtleSk7XG4gIH0sXG4gIHNldDogZnVuY3Rpb24gKGtleSwgdmFsdWUpIHtcbiAgICB2YXIgZW50cnkgPSBmaW5kVW5jYXVnaHRGcm96ZW4odGhpcywga2V5KTtcbiAgICBpZiAoZW50cnkpIGVudHJ5WzFdID0gdmFsdWU7XG4gICAgZWxzZSB0aGlzLmEucHVzaChba2V5LCB2YWx1ZV0pO1xuICB9LFxuICAnZGVsZXRlJzogZnVuY3Rpb24gKGtleSkge1xuICAgIHZhciBpbmRleCA9IGFycmF5RmluZEluZGV4KHRoaXMuYSwgZnVuY3Rpb24gKGl0KSB7XG4gICAgICByZXR1cm4gaXRbMF0gPT09IGtleTtcbiAgICB9KTtcbiAgICBpZiAofmluZGV4KSB0aGlzLmEuc3BsaWNlKGluZGV4LCAxKTtcbiAgICByZXR1cm4gISF+aW5kZXg7XG4gIH1cbn07XG5cbm1vZHVsZS5leHBvcnRzID0ge1xuICBnZXRDb25zdHJ1Y3RvcjogZnVuY3Rpb24gKHdyYXBwZXIsIE5BTUUsIElTX01BUCwgQURERVIpIHtcbiAgICB2YXIgQyA9IHdyYXBwZXIoZnVuY3Rpb24gKHRoYXQsIGl0ZXJhYmxlKSB7XG4gICAgICBhbkluc3RhbmNlKHRoYXQsIEMsIE5BTUUsICdfaScpO1xuICAgICAgdGhhdC5fdCA9IE5BTUU7ICAgICAgLy8gY29sbGVjdGlvbiB0eXBlXG4gICAgICB0aGF0Ll9pID0gaWQrKzsgICAgICAvLyBjb2xsZWN0aW9uIGlkXG4gICAgICB0aGF0Ll9sID0gdW5kZWZpbmVkOyAvLyBsZWFrIHN0b3JlIGZvciB1bmNhdWdodCBmcm96ZW4gb2JqZWN0c1xuICAgICAgaWYgKGl0ZXJhYmxlICE9IHVuZGVmaW5lZCkgZm9yT2YoaXRlcmFibGUsIElTX01BUCwgdGhhdFtBRERFUl0sIHRoYXQpO1xuICAgIH0pO1xuICAgIHJlZGVmaW5lQWxsKEMucHJvdG90eXBlLCB7XG4gICAgICAvLyAyMy4zLjMuMiBXZWFrTWFwLnByb3RvdHlwZS5kZWxldGUoa2V5KVxuICAgICAgLy8gMjMuNC4zLjMgV2Vha1NldC5wcm90b3R5cGUuZGVsZXRlKHZhbHVlKVxuICAgICAgJ2RlbGV0ZSc6IGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgaWYgKCFpc09iamVjdChrZXkpKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIHZhciBkYXRhID0gZ2V0V2VhayhrZXkpO1xuICAgICAgICBpZiAoZGF0YSA9PT0gdHJ1ZSkgcmV0dXJuIHVuY2F1Z2h0RnJvemVuU3RvcmUodmFsaWRhdGUodGhpcywgTkFNRSkpWydkZWxldGUnXShrZXkpO1xuICAgICAgICByZXR1cm4gZGF0YSAmJiAkaGFzKGRhdGEsIHRoaXMuX2kpICYmIGRlbGV0ZSBkYXRhW3RoaXMuX2ldO1xuICAgICAgfSxcbiAgICAgIC8vIDIzLjMuMy40IFdlYWtNYXAucHJvdG90eXBlLmhhcyhrZXkpXG4gICAgICAvLyAyMy40LjMuNCBXZWFrU2V0LnByb3RvdHlwZS5oYXModmFsdWUpXG4gICAgICBoYXM6IGZ1bmN0aW9uIGhhcyhrZXkpIHtcbiAgICAgICAgaWYgKCFpc09iamVjdChrZXkpKSByZXR1cm4gZmFsc2U7XG4gICAgICAgIHZhciBkYXRhID0gZ2V0V2VhayhrZXkpO1xuICAgICAgICBpZiAoZGF0YSA9PT0gdHJ1ZSkgcmV0dXJuIHVuY2F1Z2h0RnJvemVuU3RvcmUodmFsaWRhdGUodGhpcywgTkFNRSkpLmhhcyhrZXkpO1xuICAgICAgICByZXR1cm4gZGF0YSAmJiAkaGFzKGRhdGEsIHRoaXMuX2kpO1xuICAgICAgfVxuICAgIH0pO1xuICAgIHJldHVybiBDO1xuICB9LFxuICBkZWY6IGZ1bmN0aW9uICh0aGF0LCBrZXksIHZhbHVlKSB7XG4gICAgdmFyIGRhdGEgPSBnZXRXZWFrKGFuT2JqZWN0KGtleSksIHRydWUpO1xuICAgIGlmIChkYXRhID09PSB0cnVlKSB1bmNhdWdodEZyb3plblN0b3JlKHRoYXQpLnNldChrZXksIHZhbHVlKTtcbiAgICBlbHNlIGRhdGFbdGhhdC5faV0gPSB2YWx1ZTtcbiAgICByZXR1cm4gdGhhdDtcbiAgfSxcbiAgdWZzdG9yZTogdW5jYXVnaHRGcm96ZW5TdG9yZVxufTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgcmVkZWZpbmUgPSByZXF1aXJlKCcuL19yZWRlZmluZScpO1xudmFyIHJlZGVmaW5lQWxsID0gcmVxdWlyZSgnLi9fcmVkZWZpbmUtYWxsJyk7XG52YXIgbWV0YSA9IHJlcXVpcmUoJy4vX21ldGEnKTtcbnZhciBmb3JPZiA9IHJlcXVpcmUoJy4vX2Zvci1vZicpO1xudmFyIGFuSW5zdGFuY2UgPSByZXF1aXJlKCcuL19hbi1pbnN0YW5jZScpO1xudmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG52YXIgZmFpbHMgPSByZXF1aXJlKCcuL19mYWlscycpO1xudmFyICRpdGVyRGV0ZWN0ID0gcmVxdWlyZSgnLi9faXRlci1kZXRlY3QnKTtcbnZhciBzZXRUb1N0cmluZ1RhZyA9IHJlcXVpcmUoJy4vX3NldC10by1zdHJpbmctdGFnJyk7XG52YXIgaW5oZXJpdElmUmVxdWlyZWQgPSByZXF1aXJlKCcuL19pbmhlcml0LWlmLXJlcXVpcmVkJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKE5BTUUsIHdyYXBwZXIsIG1ldGhvZHMsIGNvbW1vbiwgSVNfTUFQLCBJU19XRUFLKSB7XG4gIHZhciBCYXNlID0gZ2xvYmFsW05BTUVdO1xuICB2YXIgQyA9IEJhc2U7XG4gIHZhciBBRERFUiA9IElTX01BUCA/ICdzZXQnIDogJ2FkZCc7XG4gIHZhciBwcm90byA9IEMgJiYgQy5wcm90b3R5cGU7XG4gIHZhciBPID0ge307XG4gIHZhciBmaXhNZXRob2QgPSBmdW5jdGlvbiAoS0VZKSB7XG4gICAgdmFyIGZuID0gcHJvdG9bS0VZXTtcbiAgICByZWRlZmluZShwcm90bywgS0VZLFxuICAgICAgS0VZID09ICdkZWxldGUnID8gZnVuY3Rpb24gKGEpIHtcbiAgICAgICAgcmV0dXJuIElTX1dFQUsgJiYgIWlzT2JqZWN0KGEpID8gZmFsc2UgOiBmbi5jYWxsKHRoaXMsIGEgPT09IDAgPyAwIDogYSk7XG4gICAgICB9IDogS0VZID09ICdoYXMnID8gZnVuY3Rpb24gaGFzKGEpIHtcbiAgICAgICAgcmV0dXJuIElTX1dFQUsgJiYgIWlzT2JqZWN0KGEpID8gZmFsc2UgOiBmbi5jYWxsKHRoaXMsIGEgPT09IDAgPyAwIDogYSk7XG4gICAgICB9IDogS0VZID09ICdnZXQnID8gZnVuY3Rpb24gZ2V0KGEpIHtcbiAgICAgICAgcmV0dXJuIElTX1dFQUsgJiYgIWlzT2JqZWN0KGEpID8gdW5kZWZpbmVkIDogZm4uY2FsbCh0aGlzLCBhID09PSAwID8gMCA6IGEpO1xuICAgICAgfSA6IEtFWSA9PSAnYWRkJyA/IGZ1bmN0aW9uIGFkZChhKSB7IGZuLmNhbGwodGhpcywgYSA9PT0gMCA/IDAgOiBhKTsgcmV0dXJuIHRoaXM7IH1cbiAgICAgICAgOiBmdW5jdGlvbiBzZXQoYSwgYikgeyBmbi5jYWxsKHRoaXMsIGEgPT09IDAgPyAwIDogYSwgYik7IHJldHVybiB0aGlzOyB9XG4gICAgKTtcbiAgfTtcbiAgaWYgKHR5cGVvZiBDICE9ICdmdW5jdGlvbicgfHwgIShJU19XRUFLIHx8IHByb3RvLmZvckVhY2ggJiYgIWZhaWxzKGZ1bmN0aW9uICgpIHtcbiAgICBuZXcgQygpLmVudHJpZXMoKS5uZXh0KCk7XG4gIH0pKSkge1xuICAgIC8vIGNyZWF0ZSBjb2xsZWN0aW9uIGNvbnN0cnVjdG9yXG4gICAgQyA9IGNvbW1vbi5nZXRDb25zdHJ1Y3Rvcih3cmFwcGVyLCBOQU1FLCBJU19NQVAsIEFEREVSKTtcbiAgICByZWRlZmluZUFsbChDLnByb3RvdHlwZSwgbWV0aG9kcyk7XG4gICAgbWV0YS5ORUVEID0gdHJ1ZTtcbiAgfSBlbHNlIHtcbiAgICB2YXIgaW5zdGFuY2UgPSBuZXcgQygpO1xuICAgIC8vIGVhcmx5IGltcGxlbWVudGF0aW9ucyBub3Qgc3VwcG9ydHMgY2hhaW5pbmdcbiAgICB2YXIgSEFTTlRfQ0hBSU5JTkcgPSBpbnN0YW5jZVtBRERFUl0oSVNfV0VBSyA/IHt9IDogLTAsIDEpICE9IGluc3RhbmNlO1xuICAgIC8vIFY4IH4gIENocm9taXVtIDQwLSB3ZWFrLWNvbGxlY3Rpb25zIHRocm93cyBvbiBwcmltaXRpdmVzLCBidXQgc2hvdWxkIHJldHVybiBmYWxzZVxuICAgIHZhciBUSFJPV1NfT05fUFJJTUlUSVZFUyA9IGZhaWxzKGZ1bmN0aW9uICgpIHsgaW5zdGFuY2UuaGFzKDEpOyB9KTtcbiAgICAvLyBtb3N0IGVhcmx5IGltcGxlbWVudGF0aW9ucyBkb2Vzbid0IHN1cHBvcnRzIGl0ZXJhYmxlcywgbW9zdCBtb2Rlcm4gLSBub3QgY2xvc2UgaXQgY29ycmVjdGx5XG4gICAgdmFyIEFDQ0VQVF9JVEVSQUJMRVMgPSAkaXRlckRldGVjdChmdW5jdGlvbiAoaXRlcikgeyBuZXcgQyhpdGVyKTsgfSk7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tbmV3XG4gICAgLy8gZm9yIGVhcmx5IGltcGxlbWVudGF0aW9ucyAtMCBhbmQgKzAgbm90IHRoZSBzYW1lXG4gICAgdmFyIEJVR0dZX1pFUk8gPSAhSVNfV0VBSyAmJiBmYWlscyhmdW5jdGlvbiAoKSB7XG4gICAgICAvLyBWOCB+IENocm9taXVtIDQyLSBmYWlscyBvbmx5IHdpdGggNSsgZWxlbWVudHNcbiAgICAgIHZhciAkaW5zdGFuY2UgPSBuZXcgQygpO1xuICAgICAgdmFyIGluZGV4ID0gNTtcbiAgICAgIHdoaWxlIChpbmRleC0tKSAkaW5zdGFuY2VbQURERVJdKGluZGV4LCBpbmRleCk7XG4gICAgICByZXR1cm4gISRpbnN0YW5jZS5oYXMoLTApO1xuICAgIH0pO1xuICAgIGlmICghQUNDRVBUX0lURVJBQkxFUykge1xuICAgICAgQyA9IHdyYXBwZXIoZnVuY3Rpb24gKHRhcmdldCwgaXRlcmFibGUpIHtcbiAgICAgICAgYW5JbnN0YW5jZSh0YXJnZXQsIEMsIE5BTUUpO1xuICAgICAgICB2YXIgdGhhdCA9IGluaGVyaXRJZlJlcXVpcmVkKG5ldyBCYXNlKCksIHRhcmdldCwgQyk7XG4gICAgICAgIGlmIChpdGVyYWJsZSAhPSB1bmRlZmluZWQpIGZvck9mKGl0ZXJhYmxlLCBJU19NQVAsIHRoYXRbQURERVJdLCB0aGF0KTtcbiAgICAgICAgcmV0dXJuIHRoYXQ7XG4gICAgICB9KTtcbiAgICAgIEMucHJvdG90eXBlID0gcHJvdG87XG4gICAgICBwcm90by5jb25zdHJ1Y3RvciA9IEM7XG4gICAgfVxuICAgIGlmIChUSFJPV1NfT05fUFJJTUlUSVZFUyB8fCBCVUdHWV9aRVJPKSB7XG4gICAgICBmaXhNZXRob2QoJ2RlbGV0ZScpO1xuICAgICAgZml4TWV0aG9kKCdoYXMnKTtcbiAgICAgIElTX01BUCAmJiBmaXhNZXRob2QoJ2dldCcpO1xuICAgIH1cbiAgICBpZiAoQlVHR1lfWkVSTyB8fCBIQVNOVF9DSEFJTklORykgZml4TWV0aG9kKEFEREVSKTtcbiAgICAvLyB3ZWFrIGNvbGxlY3Rpb25zIHNob3VsZCBub3QgY29udGFpbnMgLmNsZWFyIG1ldGhvZFxuICAgIGlmIChJU19XRUFLICYmIHByb3RvLmNsZWFyKSBkZWxldGUgcHJvdG8uY2xlYXI7XG4gIH1cblxuICBzZXRUb1N0cmluZ1RhZyhDLCBOQU1FKTtcblxuICBPW05BTUVdID0gQztcbiAgJGV4cG9ydCgkZXhwb3J0LkcgKyAkZXhwb3J0LlcgKyAkZXhwb3J0LkYgKiAoQyAhPSBCYXNlKSwgTyk7XG5cbiAgaWYgKCFJU19XRUFLKSBjb21tb24uc2V0U3Ryb25nKEMsIE5BTUUsIElTX01BUCk7XG5cbiAgcmV0dXJuIEM7XG59O1xuIiwidmFyIGNvcmUgPSBtb2R1bGUuZXhwb3J0cyA9IHsgdmVyc2lvbjogJzIuNi40JyB9O1xuaWYgKHR5cGVvZiBfX2UgPT0gJ251bWJlcicpIF9fZSA9IGNvcmU7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW5kZWZcbiIsIid1c2Ugc3RyaWN0JztcbnZhciAkZGVmaW5lUHJvcGVydHkgPSByZXF1aXJlKCcuL19vYmplY3QtZHAnKTtcbnZhciBjcmVhdGVEZXNjID0gcmVxdWlyZSgnLi9fcHJvcGVydHktZGVzYycpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChvYmplY3QsIGluZGV4LCB2YWx1ZSkge1xuICBpZiAoaW5kZXggaW4gb2JqZWN0KSAkZGVmaW5lUHJvcGVydHkuZihvYmplY3QsIGluZGV4LCBjcmVhdGVEZXNjKDAsIHZhbHVlKSk7XG4gIGVsc2Ugb2JqZWN0W2luZGV4XSA9IHZhbHVlO1xufTtcbiIsIi8vIG9wdGlvbmFsIC8gc2ltcGxlIGNvbnRleHQgYmluZGluZ1xudmFyIGFGdW5jdGlvbiA9IHJlcXVpcmUoJy4vX2EtZnVuY3Rpb24nKTtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGZuLCB0aGF0LCBsZW5ndGgpIHtcbiAgYUZ1bmN0aW9uKGZuKTtcbiAgaWYgKHRoYXQgPT09IHVuZGVmaW5lZCkgcmV0dXJuIGZuO1xuICBzd2l0Y2ggKGxlbmd0aCkge1xuICAgIGNhc2UgMTogcmV0dXJuIGZ1bmN0aW9uIChhKSB7XG4gICAgICByZXR1cm4gZm4uY2FsbCh0aGF0LCBhKTtcbiAgICB9O1xuICAgIGNhc2UgMjogcmV0dXJuIGZ1bmN0aW9uIChhLCBiKSB7XG4gICAgICByZXR1cm4gZm4uY2FsbCh0aGF0LCBhLCBiKTtcbiAgICB9O1xuICAgIGNhc2UgMzogcmV0dXJuIGZ1bmN0aW9uIChhLCBiLCBjKSB7XG4gICAgICByZXR1cm4gZm4uY2FsbCh0aGF0LCBhLCBiLCBjKTtcbiAgICB9O1xuICB9XG4gIHJldHVybiBmdW5jdGlvbiAoLyogLi4uYXJncyAqLykge1xuICAgIHJldHVybiBmbi5hcHBseSh0aGF0LCBhcmd1bWVudHMpO1xuICB9O1xufTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIDIwLjMuNC4zNiAvIDE1LjkuNS40MyBEYXRlLnByb3RvdHlwZS50b0lTT1N0cmluZygpXG52YXIgZmFpbHMgPSByZXF1aXJlKCcuL19mYWlscycpO1xudmFyIGdldFRpbWUgPSBEYXRlLnByb3RvdHlwZS5nZXRUaW1lO1xudmFyICR0b0lTT1N0cmluZyA9IERhdGUucHJvdG90eXBlLnRvSVNPU3RyaW5nO1xuXG52YXIgbHogPSBmdW5jdGlvbiAobnVtKSB7XG4gIHJldHVybiBudW0gPiA5ID8gbnVtIDogJzAnICsgbnVtO1xufTtcblxuLy8gUGhhbnRvbUpTIC8gb2xkIFdlYktpdCBoYXMgYSBicm9rZW4gaW1wbGVtZW50YXRpb25zXG5tb2R1bGUuZXhwb3J0cyA9IChmYWlscyhmdW5jdGlvbiAoKSB7XG4gIHJldHVybiAkdG9JU09TdHJpbmcuY2FsbChuZXcgRGF0ZSgtNWUxMyAtIDEpKSAhPSAnMDM4NS0wNy0yNVQwNzowNjozOS45OTlaJztcbn0pIHx8ICFmYWlscyhmdW5jdGlvbiAoKSB7XG4gICR0b0lTT1N0cmluZy5jYWxsKG5ldyBEYXRlKE5hTikpO1xufSkpID8gZnVuY3Rpb24gdG9JU09TdHJpbmcoKSB7XG4gIGlmICghaXNGaW5pdGUoZ2V0VGltZS5jYWxsKHRoaXMpKSkgdGhyb3cgUmFuZ2VFcnJvcignSW52YWxpZCB0aW1lIHZhbHVlJyk7XG4gIHZhciBkID0gdGhpcztcbiAgdmFyIHkgPSBkLmdldFVUQ0Z1bGxZZWFyKCk7XG4gIHZhciBtID0gZC5nZXRVVENNaWxsaXNlY29uZHMoKTtcbiAgdmFyIHMgPSB5IDwgMCA/ICctJyA6IHkgPiA5OTk5ID8gJysnIDogJyc7XG4gIHJldHVybiBzICsgKCcwMDAwMCcgKyBNYXRoLmFicyh5KSkuc2xpY2UocyA/IC02IDogLTQpICtcbiAgICAnLScgKyBseihkLmdldFVUQ01vbnRoKCkgKyAxKSArICctJyArIGx6KGQuZ2V0VVRDRGF0ZSgpKSArXG4gICAgJ1QnICsgbHooZC5nZXRVVENIb3VycygpKSArICc6JyArIGx6KGQuZ2V0VVRDTWludXRlcygpKSArXG4gICAgJzonICsgbHooZC5nZXRVVENTZWNvbmRzKCkpICsgJy4nICsgKG0gPiA5OSA/IG0gOiAnMCcgKyBseihtKSkgKyAnWic7XG59IDogJHRvSVNPU3RyaW5nO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgdG9QcmltaXRpdmUgPSByZXF1aXJlKCcuL190by1wcmltaXRpdmUnKTtcbnZhciBOVU1CRVIgPSAnbnVtYmVyJztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaGludCkge1xuICBpZiAoaGludCAhPT0gJ3N0cmluZycgJiYgaGludCAhPT0gTlVNQkVSICYmIGhpbnQgIT09ICdkZWZhdWx0JykgdGhyb3cgVHlwZUVycm9yKCdJbmNvcnJlY3QgaGludCcpO1xuICByZXR1cm4gdG9QcmltaXRpdmUoYW5PYmplY3QodGhpcyksIGhpbnQgIT0gTlVNQkVSKTtcbn07XG4iLCIvLyA3LjIuMSBSZXF1aXJlT2JqZWN0Q29lcmNpYmxlKGFyZ3VtZW50KVxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaXQpIHtcbiAgaWYgKGl0ID09IHVuZGVmaW5lZCkgdGhyb3cgVHlwZUVycm9yKFwiQ2FuJ3QgY2FsbCBtZXRob2Qgb24gIFwiICsgaXQpO1xuICByZXR1cm4gaXQ7XG59O1xuIiwiLy8gVGhhbmsncyBJRTggZm9yIGhpcyBmdW5ueSBkZWZpbmVQcm9wZXJ0eVxubW9kdWxlLmV4cG9ydHMgPSAhcmVxdWlyZSgnLi9fZmFpbHMnKShmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkoe30sICdhJywgeyBnZXQ6IGZ1bmN0aW9uICgpIHsgcmV0dXJuIDc7IH0gfSkuYSAhPSA3O1xufSk7XG4iLCJ2YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcbnZhciBkb2N1bWVudCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpLmRvY3VtZW50O1xuLy8gdHlwZW9mIGRvY3VtZW50LmNyZWF0ZUVsZW1lbnQgaXMgJ29iamVjdCcgaW4gb2xkIElFXG52YXIgaXMgPSBpc09iamVjdChkb2N1bWVudCkgJiYgaXNPYmplY3QoZG9jdW1lbnQuY3JlYXRlRWxlbWVudCk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCkge1xuICByZXR1cm4gaXMgPyBkb2N1bWVudC5jcmVhdGVFbGVtZW50KGl0KSA6IHt9O1xufTtcbiIsIi8vIElFIDgtIGRvbid0IGVudW0gYnVnIGtleXNcbm1vZHVsZS5leHBvcnRzID0gKFxuICAnY29uc3RydWN0b3IsaGFzT3duUHJvcGVydHksaXNQcm90b3R5cGVPZixwcm9wZXJ0eUlzRW51bWVyYWJsZSx0b0xvY2FsZVN0cmluZyx0b1N0cmluZyx2YWx1ZU9mJ1xuKS5zcGxpdCgnLCcpO1xuIiwiLy8gYWxsIGVudW1lcmFibGUgb2JqZWN0IGtleXMsIGluY2x1ZGVzIHN5bWJvbHNcbnZhciBnZXRLZXlzID0gcmVxdWlyZSgnLi9fb2JqZWN0LWtleXMnKTtcbnZhciBnT1BTID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcHMnKTtcbnZhciBwSUUgPSByZXF1aXJlKCcuL19vYmplY3QtcGllJyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCkge1xuICB2YXIgcmVzdWx0ID0gZ2V0S2V5cyhpdCk7XG4gIHZhciBnZXRTeW1ib2xzID0gZ09QUy5mO1xuICBpZiAoZ2V0U3ltYm9scykge1xuICAgIHZhciBzeW1ib2xzID0gZ2V0U3ltYm9scyhpdCk7XG4gICAgdmFyIGlzRW51bSA9IHBJRS5mO1xuICAgIHZhciBpID0gMDtcbiAgICB2YXIga2V5O1xuICAgIHdoaWxlIChzeW1ib2xzLmxlbmd0aCA+IGkpIGlmIChpc0VudW0uY2FsbChpdCwga2V5ID0gc3ltYm9sc1tpKytdKSkgcmVzdWx0LnB1c2goa2V5KTtcbiAgfSByZXR1cm4gcmVzdWx0O1xufTtcbiIsInZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciBjb3JlID0gcmVxdWlyZSgnLi9fY29yZScpO1xudmFyIGhpZGUgPSByZXF1aXJlKCcuL19oaWRlJyk7XG52YXIgcmVkZWZpbmUgPSByZXF1aXJlKCcuL19yZWRlZmluZScpO1xudmFyIGN0eCA9IHJlcXVpcmUoJy4vX2N0eCcpO1xudmFyIFBST1RPVFlQRSA9ICdwcm90b3R5cGUnO1xuXG52YXIgJGV4cG9ydCA9IGZ1bmN0aW9uICh0eXBlLCBuYW1lLCBzb3VyY2UpIHtcbiAgdmFyIElTX0ZPUkNFRCA9IHR5cGUgJiAkZXhwb3J0LkY7XG4gIHZhciBJU19HTE9CQUwgPSB0eXBlICYgJGV4cG9ydC5HO1xuICB2YXIgSVNfU1RBVElDID0gdHlwZSAmICRleHBvcnQuUztcbiAgdmFyIElTX1BST1RPID0gdHlwZSAmICRleHBvcnQuUDtcbiAgdmFyIElTX0JJTkQgPSB0eXBlICYgJGV4cG9ydC5CO1xuICB2YXIgdGFyZ2V0ID0gSVNfR0xPQkFMID8gZ2xvYmFsIDogSVNfU1RBVElDID8gZ2xvYmFsW25hbWVdIHx8IChnbG9iYWxbbmFtZV0gPSB7fSkgOiAoZ2xvYmFsW25hbWVdIHx8IHt9KVtQUk9UT1RZUEVdO1xuICB2YXIgZXhwb3J0cyA9IElTX0dMT0JBTCA/IGNvcmUgOiBjb3JlW25hbWVdIHx8IChjb3JlW25hbWVdID0ge30pO1xuICB2YXIgZXhwUHJvdG8gPSBleHBvcnRzW1BST1RPVFlQRV0gfHwgKGV4cG9ydHNbUFJPVE9UWVBFXSA9IHt9KTtcbiAgdmFyIGtleSwgb3duLCBvdXQsIGV4cDtcbiAgaWYgKElTX0dMT0JBTCkgc291cmNlID0gbmFtZTtcbiAgZm9yIChrZXkgaW4gc291cmNlKSB7XG4gICAgLy8gY29udGFpbnMgaW4gbmF0aXZlXG4gICAgb3duID0gIUlTX0ZPUkNFRCAmJiB0YXJnZXQgJiYgdGFyZ2V0W2tleV0gIT09IHVuZGVmaW5lZDtcbiAgICAvLyBleHBvcnQgbmF0aXZlIG9yIHBhc3NlZFxuICAgIG91dCA9IChvd24gPyB0YXJnZXQgOiBzb3VyY2UpW2tleV07XG4gICAgLy8gYmluZCB0aW1lcnMgdG8gZ2xvYmFsIGZvciBjYWxsIGZyb20gZXhwb3J0IGNvbnRleHRcbiAgICBleHAgPSBJU19CSU5EICYmIG93biA/IGN0eChvdXQsIGdsb2JhbCkgOiBJU19QUk9UTyAmJiB0eXBlb2Ygb3V0ID09ICdmdW5jdGlvbicgPyBjdHgoRnVuY3Rpb24uY2FsbCwgb3V0KSA6IG91dDtcbiAgICAvLyBleHRlbmQgZ2xvYmFsXG4gICAgaWYgKHRhcmdldCkgcmVkZWZpbmUodGFyZ2V0LCBrZXksIG91dCwgdHlwZSAmICRleHBvcnQuVSk7XG4gICAgLy8gZXhwb3J0XG4gICAgaWYgKGV4cG9ydHNba2V5XSAhPSBvdXQpIGhpZGUoZXhwb3J0cywga2V5LCBleHApO1xuICAgIGlmIChJU19QUk9UTyAmJiBleHBQcm90b1trZXldICE9IG91dCkgZXhwUHJvdG9ba2V5XSA9IG91dDtcbiAgfVxufTtcbmdsb2JhbC5jb3JlID0gY29yZTtcbi8vIHR5cGUgYml0bWFwXG4kZXhwb3J0LkYgPSAxOyAgIC8vIGZvcmNlZFxuJGV4cG9ydC5HID0gMjsgICAvLyBnbG9iYWxcbiRleHBvcnQuUyA9IDQ7ICAgLy8gc3RhdGljXG4kZXhwb3J0LlAgPSA4OyAgIC8vIHByb3RvXG4kZXhwb3J0LkIgPSAxNjsgIC8vIGJpbmRcbiRleHBvcnQuVyA9IDMyOyAgLy8gd3JhcFxuJGV4cG9ydC5VID0gNjQ7ICAvLyBzYWZlXG4kZXhwb3J0LlIgPSAxMjg7IC8vIHJlYWwgcHJvdG8gbWV0aG9kIGZvciBgbGlicmFyeWBcbm1vZHVsZS5leHBvcnRzID0gJGV4cG9ydDtcbiIsInZhciBNQVRDSCA9IHJlcXVpcmUoJy4vX3drcycpKCdtYXRjaCcpO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoS0VZKSB7XG4gIHZhciByZSA9IC8uLztcbiAgdHJ5IHtcbiAgICAnLy4vJ1tLRVldKHJlKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIHRyeSB7XG4gICAgICByZVtNQVRDSF0gPSBmYWxzZTtcbiAgICAgIHJldHVybiAhJy8uLydbS0VZXShyZSk7XG4gICAgfSBjYXRjaCAoZikgeyAvKiBlbXB0eSAqLyB9XG4gIH0gcmV0dXJuIHRydWU7XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoZXhlYykge1xuICB0cnkge1xuICAgIHJldHVybiAhIWV4ZWMoKTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIHJldHVybiB0cnVlO1xuICB9XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xucmVxdWlyZSgnLi9lczYucmVnZXhwLmV4ZWMnKTtcbnZhciByZWRlZmluZSA9IHJlcXVpcmUoJy4vX3JlZGVmaW5lJyk7XG52YXIgaGlkZSA9IHJlcXVpcmUoJy4vX2hpZGUnKTtcbnZhciBmYWlscyA9IHJlcXVpcmUoJy4vX2ZhaWxzJyk7XG52YXIgZGVmaW5lZCA9IHJlcXVpcmUoJy4vX2RlZmluZWQnKTtcbnZhciB3a3MgPSByZXF1aXJlKCcuL193a3MnKTtcbnZhciByZWdleHBFeGVjID0gcmVxdWlyZSgnLi9fcmVnZXhwLWV4ZWMnKTtcblxudmFyIFNQRUNJRVMgPSB3a3MoJ3NwZWNpZXMnKTtcblxudmFyIFJFUExBQ0VfU1VQUE9SVFNfTkFNRURfR1JPVVBTID0gIWZhaWxzKGZ1bmN0aW9uICgpIHtcbiAgLy8gI3JlcGxhY2UgbmVlZHMgYnVpbHQtaW4gc3VwcG9ydCBmb3IgbmFtZWQgZ3JvdXBzLlxuICAvLyAjbWF0Y2ggd29ya3MgZmluZSBiZWNhdXNlIGl0IGp1c3QgcmV0dXJuIHRoZSBleGVjIHJlc3VsdHMsIGV2ZW4gaWYgaXQgaGFzXG4gIC8vIGEgXCJncm9wc1wiIHByb3BlcnR5LlxuICB2YXIgcmUgPSAvLi87XG4gIHJlLmV4ZWMgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHJlc3VsdCA9IFtdO1xuICAgIHJlc3VsdC5ncm91cHMgPSB7IGE6ICc3JyB9O1xuICAgIHJldHVybiByZXN1bHQ7XG4gIH07XG4gIHJldHVybiAnJy5yZXBsYWNlKHJlLCAnJDxhPicpICE9PSAnNyc7XG59KTtcblxudmFyIFNQTElUX1dPUktTX1dJVEhfT1ZFUldSSVRURU5fRVhFQyA9IChmdW5jdGlvbiAoKSB7XG4gIC8vIENocm9tZSA1MSBoYXMgYSBidWdneSBcInNwbGl0XCIgaW1wbGVtZW50YXRpb24gd2hlbiBSZWdFeHAjZXhlYyAhPT0gbmF0aXZlRXhlY1xuICB2YXIgcmUgPSAvKD86KS87XG4gIHZhciBvcmlnaW5hbEV4ZWMgPSByZS5leGVjO1xuICByZS5leGVjID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gb3JpZ2luYWxFeGVjLmFwcGx5KHRoaXMsIGFyZ3VtZW50cyk7IH07XG4gIHZhciByZXN1bHQgPSAnYWInLnNwbGl0KHJlKTtcbiAgcmV0dXJuIHJlc3VsdC5sZW5ndGggPT09IDIgJiYgcmVzdWx0WzBdID09PSAnYScgJiYgcmVzdWx0WzFdID09PSAnYic7XG59KSgpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChLRVksIGxlbmd0aCwgZXhlYykge1xuICB2YXIgU1lNQk9MID0gd2tzKEtFWSk7XG5cbiAgdmFyIERFTEVHQVRFU19UT19TWU1CT0wgPSAhZmFpbHMoZnVuY3Rpb24gKCkge1xuICAgIC8vIFN0cmluZyBtZXRob2RzIGNhbGwgc3ltYm9sLW5hbWVkIFJlZ0VwIG1ldGhvZHNcbiAgICB2YXIgTyA9IHt9O1xuICAgIE9bU1lNQk9MXSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIDc7IH07XG4gICAgcmV0dXJuICcnW0tFWV0oTykgIT0gNztcbiAgfSk7XG5cbiAgdmFyIERFTEVHQVRFU19UT19FWEVDID0gREVMRUdBVEVTX1RPX1NZTUJPTCA/ICFmYWlscyhmdW5jdGlvbiAoKSB7XG4gICAgLy8gU3ltYm9sLW5hbWVkIFJlZ0V4cCBtZXRob2RzIGNhbGwgLmV4ZWNcbiAgICB2YXIgZXhlY0NhbGxlZCA9IGZhbHNlO1xuICAgIHZhciByZSA9IC9hLztcbiAgICByZS5leGVjID0gZnVuY3Rpb24gKCkgeyBleGVjQ2FsbGVkID0gdHJ1ZTsgcmV0dXJuIG51bGw7IH07XG4gICAgaWYgKEtFWSA9PT0gJ3NwbGl0Jykge1xuICAgICAgLy8gUmVnRXhwW0BAc3BsaXRdIGRvZXNuJ3QgY2FsbCB0aGUgcmVnZXgncyBleGVjIG1ldGhvZCwgYnV0IGZpcnN0IGNyZWF0ZXNcbiAgICAgIC8vIGEgbmV3IG9uZS4gV2UgbmVlZCB0byByZXR1cm4gdGhlIHBhdGNoZWQgcmVnZXggd2hlbiBjcmVhdGluZyB0aGUgbmV3IG9uZS5cbiAgICAgIHJlLmNvbnN0cnVjdG9yID0ge307XG4gICAgICByZS5jb25zdHJ1Y3RvcltTUEVDSUVTXSA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHJlOyB9O1xuICAgIH1cbiAgICByZVtTWU1CT0xdKCcnKTtcbiAgICByZXR1cm4gIWV4ZWNDYWxsZWQ7XG4gIH0pIDogdW5kZWZpbmVkO1xuXG4gIGlmIChcbiAgICAhREVMRUdBVEVTX1RPX1NZTUJPTCB8fFxuICAgICFERUxFR0FURVNfVE9fRVhFQyB8fFxuICAgIChLRVkgPT09ICdyZXBsYWNlJyAmJiAhUkVQTEFDRV9TVVBQT1JUU19OQU1FRF9HUk9VUFMpIHx8XG4gICAgKEtFWSA9PT0gJ3NwbGl0JyAmJiAhU1BMSVRfV09SS1NfV0lUSF9PVkVSV1JJVFRFTl9FWEVDKVxuICApIHtcbiAgICB2YXIgbmF0aXZlUmVnRXhwTWV0aG9kID0gLy4vW1NZTUJPTF07XG4gICAgdmFyIGZucyA9IGV4ZWMoXG4gICAgICBkZWZpbmVkLFxuICAgICAgU1lNQk9MLFxuICAgICAgJydbS0VZXSxcbiAgICAgIGZ1bmN0aW9uIG1heWJlQ2FsbE5hdGl2ZShuYXRpdmVNZXRob2QsIHJlZ2V4cCwgc3RyLCBhcmcyLCBmb3JjZVN0cmluZ01ldGhvZCkge1xuICAgICAgICBpZiAocmVnZXhwLmV4ZWMgPT09IHJlZ2V4cEV4ZWMpIHtcbiAgICAgICAgICBpZiAoREVMRUdBVEVTX1RPX1NZTUJPTCAmJiAhZm9yY2VTdHJpbmdNZXRob2QpIHtcbiAgICAgICAgICAgIC8vIFRoZSBuYXRpdmUgU3RyaW5nIG1ldGhvZCBhbHJlYWR5IGRlbGVnYXRlcyB0byBAQG1ldGhvZCAodGhpc1xuICAgICAgICAgICAgLy8gcG9seWZpbGxlZCBmdW5jdGlvbiksIGxlYXNpbmcgdG8gaW5maW5pdGUgcmVjdXJzaW9uLlxuICAgICAgICAgICAgLy8gV2UgYXZvaWQgaXQgYnkgZGlyZWN0bHkgY2FsbGluZyB0aGUgbmF0aXZlIEBAbWV0aG9kIG1ldGhvZC5cbiAgICAgICAgICAgIHJldHVybiB7IGRvbmU6IHRydWUsIHZhbHVlOiBuYXRpdmVSZWdFeHBNZXRob2QuY2FsbChyZWdleHAsIHN0ciwgYXJnMikgfTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcmV0dXJuIHsgZG9uZTogdHJ1ZSwgdmFsdWU6IG5hdGl2ZU1ldGhvZC5jYWxsKHN0ciwgcmVnZXhwLCBhcmcyKSB9O1xuICAgICAgICB9XG4gICAgICAgIHJldHVybiB7IGRvbmU6IGZhbHNlIH07XG4gICAgICB9XG4gICAgKTtcbiAgICB2YXIgc3RyZm4gPSBmbnNbMF07XG4gICAgdmFyIHJ4Zm4gPSBmbnNbMV07XG5cbiAgICByZWRlZmluZShTdHJpbmcucHJvdG90eXBlLCBLRVksIHN0cmZuKTtcbiAgICBoaWRlKFJlZ0V4cC5wcm90b3R5cGUsIFNZTUJPTCwgbGVuZ3RoID09IDJcbiAgICAgIC8vIDIxLjIuNS44IFJlZ0V4cC5wcm90b3R5cGVbQEByZXBsYWNlXShzdHJpbmcsIHJlcGxhY2VWYWx1ZSlcbiAgICAgIC8vIDIxLjIuNS4xMSBSZWdFeHAucHJvdG90eXBlW0BAc3BsaXRdKHN0cmluZywgbGltaXQpXG4gICAgICA/IGZ1bmN0aW9uIChzdHJpbmcsIGFyZykgeyByZXR1cm4gcnhmbi5jYWxsKHN0cmluZywgdGhpcywgYXJnKTsgfVxuICAgICAgLy8gMjEuMi41LjYgUmVnRXhwLnByb3RvdHlwZVtAQG1hdGNoXShzdHJpbmcpXG4gICAgICAvLyAyMS4yLjUuOSBSZWdFeHAucHJvdG90eXBlW0BAc2VhcmNoXShzdHJpbmcpXG4gICAgICA6IGZ1bmN0aW9uIChzdHJpbmcpIHsgcmV0dXJuIHJ4Zm4uY2FsbChzdHJpbmcsIHRoaXMpOyB9XG4gICAgKTtcbiAgfVxufTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIDIxLjIuNS4zIGdldCBSZWdFeHAucHJvdG90eXBlLmZsYWdzXG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKCkge1xuICB2YXIgdGhhdCA9IGFuT2JqZWN0KHRoaXMpO1xuICB2YXIgcmVzdWx0ID0gJyc7XG4gIGlmICh0aGF0Lmdsb2JhbCkgcmVzdWx0ICs9ICdnJztcbiAgaWYgKHRoYXQuaWdub3JlQ2FzZSkgcmVzdWx0ICs9ICdpJztcbiAgaWYgKHRoYXQubXVsdGlsaW5lKSByZXN1bHQgKz0gJ20nO1xuICBpZiAodGhhdC51bmljb2RlKSByZXN1bHQgKz0gJ3UnO1xuICBpZiAodGhhdC5zdGlja3kpIHJlc3VsdCArPSAneSc7XG4gIHJldHVybiByZXN1bHQ7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9wcm9wb3NhbC1mbGF0TWFwLyNzZWMtRmxhdHRlbkludG9BcnJheVxudmFyIGlzQXJyYXkgPSByZXF1aXJlKCcuL19pcy1hcnJheScpO1xudmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG52YXIgdG9MZW5ndGggPSByZXF1aXJlKCcuL190by1sZW5ndGgnKTtcbnZhciBjdHggPSByZXF1aXJlKCcuL19jdHgnKTtcbnZhciBJU19DT05DQVRfU1BSRUFEQUJMRSA9IHJlcXVpcmUoJy4vX3drcycpKCdpc0NvbmNhdFNwcmVhZGFibGUnKTtcblxuZnVuY3Rpb24gZmxhdHRlbkludG9BcnJheSh0YXJnZXQsIG9yaWdpbmFsLCBzb3VyY2UsIHNvdXJjZUxlbiwgc3RhcnQsIGRlcHRoLCBtYXBwZXIsIHRoaXNBcmcpIHtcbiAgdmFyIHRhcmdldEluZGV4ID0gc3RhcnQ7XG4gIHZhciBzb3VyY2VJbmRleCA9IDA7XG4gIHZhciBtYXBGbiA9IG1hcHBlciA/IGN0eChtYXBwZXIsIHRoaXNBcmcsIDMpIDogZmFsc2U7XG4gIHZhciBlbGVtZW50LCBzcHJlYWRhYmxlO1xuXG4gIHdoaWxlIChzb3VyY2VJbmRleCA8IHNvdXJjZUxlbikge1xuICAgIGlmIChzb3VyY2VJbmRleCBpbiBzb3VyY2UpIHtcbiAgICAgIGVsZW1lbnQgPSBtYXBGbiA/IG1hcEZuKHNvdXJjZVtzb3VyY2VJbmRleF0sIHNvdXJjZUluZGV4LCBvcmlnaW5hbCkgOiBzb3VyY2Vbc291cmNlSW5kZXhdO1xuXG4gICAgICBzcHJlYWRhYmxlID0gZmFsc2U7XG4gICAgICBpZiAoaXNPYmplY3QoZWxlbWVudCkpIHtcbiAgICAgICAgc3ByZWFkYWJsZSA9IGVsZW1lbnRbSVNfQ09OQ0FUX1NQUkVBREFCTEVdO1xuICAgICAgICBzcHJlYWRhYmxlID0gc3ByZWFkYWJsZSAhPT0gdW5kZWZpbmVkID8gISFzcHJlYWRhYmxlIDogaXNBcnJheShlbGVtZW50KTtcbiAgICAgIH1cblxuICAgICAgaWYgKHNwcmVhZGFibGUgJiYgZGVwdGggPiAwKSB7XG4gICAgICAgIHRhcmdldEluZGV4ID0gZmxhdHRlbkludG9BcnJheSh0YXJnZXQsIG9yaWdpbmFsLCBlbGVtZW50LCB0b0xlbmd0aChlbGVtZW50Lmxlbmd0aCksIHRhcmdldEluZGV4LCBkZXB0aCAtIDEpIC0gMTtcbiAgICAgIH0gZWxzZSB7XG4gICAgICAgIGlmICh0YXJnZXRJbmRleCA+PSAweDFmZmZmZmZmZmZmZmZmKSB0aHJvdyBUeXBlRXJyb3IoKTtcbiAgICAgICAgdGFyZ2V0W3RhcmdldEluZGV4XSA9IGVsZW1lbnQ7XG4gICAgICB9XG5cbiAgICAgIHRhcmdldEluZGV4Kys7XG4gICAgfVxuICAgIHNvdXJjZUluZGV4Kys7XG4gIH1cbiAgcmV0dXJuIHRhcmdldEluZGV4O1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGZsYXR0ZW5JbnRvQXJyYXk7XG4iLCJ2YXIgY3R4ID0gcmVxdWlyZSgnLi9fY3R4Jyk7XG52YXIgY2FsbCA9IHJlcXVpcmUoJy4vX2l0ZXItY2FsbCcpO1xudmFyIGlzQXJyYXlJdGVyID0gcmVxdWlyZSgnLi9faXMtYXJyYXktaXRlcicpO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgdG9MZW5ndGggPSByZXF1aXJlKCcuL190by1sZW5ndGgnKTtcbnZhciBnZXRJdGVyRm4gPSByZXF1aXJlKCcuL2NvcmUuZ2V0LWl0ZXJhdG9yLW1ldGhvZCcpO1xudmFyIEJSRUFLID0ge307XG52YXIgUkVUVVJOID0ge307XG52YXIgZXhwb3J0cyA9IG1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGl0ZXJhYmxlLCBlbnRyaWVzLCBmbiwgdGhhdCwgSVRFUkFUT1IpIHtcbiAgdmFyIGl0ZXJGbiA9IElURVJBVE9SID8gZnVuY3Rpb24gKCkgeyByZXR1cm4gaXRlcmFibGU7IH0gOiBnZXRJdGVyRm4oaXRlcmFibGUpO1xuICB2YXIgZiA9IGN0eChmbiwgdGhhdCwgZW50cmllcyA/IDIgOiAxKTtcbiAgdmFyIGluZGV4ID0gMDtcbiAgdmFyIGxlbmd0aCwgc3RlcCwgaXRlcmF0b3IsIHJlc3VsdDtcbiAgaWYgKHR5cGVvZiBpdGVyRm4gIT0gJ2Z1bmN0aW9uJykgdGhyb3cgVHlwZUVycm9yKGl0ZXJhYmxlICsgJyBpcyBub3QgaXRlcmFibGUhJyk7XG4gIC8vIGZhc3QgY2FzZSBmb3IgYXJyYXlzIHdpdGggZGVmYXVsdCBpdGVyYXRvclxuICBpZiAoaXNBcnJheUl0ZXIoaXRlckZuKSkgZm9yIChsZW5ndGggPSB0b0xlbmd0aChpdGVyYWJsZS5sZW5ndGgpOyBsZW5ndGggPiBpbmRleDsgaW5kZXgrKykge1xuICAgIHJlc3VsdCA9IGVudHJpZXMgPyBmKGFuT2JqZWN0KHN0ZXAgPSBpdGVyYWJsZVtpbmRleF0pWzBdLCBzdGVwWzFdKSA6IGYoaXRlcmFibGVbaW5kZXhdKTtcbiAgICBpZiAocmVzdWx0ID09PSBCUkVBSyB8fCByZXN1bHQgPT09IFJFVFVSTikgcmV0dXJuIHJlc3VsdDtcbiAgfSBlbHNlIGZvciAoaXRlcmF0b3IgPSBpdGVyRm4uY2FsbChpdGVyYWJsZSk7ICEoc3RlcCA9IGl0ZXJhdG9yLm5leHQoKSkuZG9uZTspIHtcbiAgICByZXN1bHQgPSBjYWxsKGl0ZXJhdG9yLCBmLCBzdGVwLnZhbHVlLCBlbnRyaWVzKTtcbiAgICBpZiAocmVzdWx0ID09PSBCUkVBSyB8fCByZXN1bHQgPT09IFJFVFVSTikgcmV0dXJuIHJlc3VsdDtcbiAgfVxufTtcbmV4cG9ydHMuQlJFQUsgPSBCUkVBSztcbmV4cG9ydHMuUkVUVVJOID0gUkVUVVJOO1xuIiwibW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKCcuL19zaGFyZWQnKSgnbmF0aXZlLWZ1bmN0aW9uLXRvLXN0cmluZycsIEZ1bmN0aW9uLnRvU3RyaW5nKTtcbiIsIi8vIGh0dHBzOi8vZ2l0aHViLmNvbS96bG9pcm9jay9jb3JlLWpzL2lzc3Vlcy84NiNpc3N1ZWNvbW1lbnQtMTE1NzU5MDI4XG52YXIgZ2xvYmFsID0gbW9kdWxlLmV4cG9ydHMgPSB0eXBlb2Ygd2luZG93ICE9ICd1bmRlZmluZWQnICYmIHdpbmRvdy5NYXRoID09IE1hdGhcbiAgPyB3aW5kb3cgOiB0eXBlb2Ygc2VsZiAhPSAndW5kZWZpbmVkJyAmJiBzZWxmLk1hdGggPT0gTWF0aCA/IHNlbGZcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLW5ldy1mdW5jXG4gIDogRnVuY3Rpb24oJ3JldHVybiB0aGlzJykoKTtcbmlmICh0eXBlb2YgX19nID09ICdudW1iZXInKSBfX2cgPSBnbG9iYWw7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW5kZWZcbiIsInZhciBoYXNPd25Qcm9wZXJ0eSA9IHt9Lmhhc093blByb3BlcnR5O1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaXQsIGtleSkge1xuICByZXR1cm4gaGFzT3duUHJvcGVydHkuY2FsbChpdCwga2V5KTtcbn07XG4iLCJ2YXIgZFAgPSByZXF1aXJlKCcuL19vYmplY3QtZHAnKTtcbnZhciBjcmVhdGVEZXNjID0gcmVxdWlyZSgnLi9fcHJvcGVydHktZGVzYycpO1xubW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKCcuL19kZXNjcmlwdG9ycycpID8gZnVuY3Rpb24gKG9iamVjdCwga2V5LCB2YWx1ZSkge1xuICByZXR1cm4gZFAuZihvYmplY3QsIGtleSwgY3JlYXRlRGVzYygxLCB2YWx1ZSkpO1xufSA6IGZ1bmN0aW9uIChvYmplY3QsIGtleSwgdmFsdWUpIHtcbiAgb2JqZWN0W2tleV0gPSB2YWx1ZTtcbiAgcmV0dXJuIG9iamVjdDtcbn07XG4iLCJ2YXIgZG9jdW1lbnQgPSByZXF1aXJlKCcuL19nbG9iYWwnKS5kb2N1bWVudDtcbm1vZHVsZS5leHBvcnRzID0gZG9jdW1lbnQgJiYgZG9jdW1lbnQuZG9jdW1lbnRFbGVtZW50O1xuIiwibW9kdWxlLmV4cG9ydHMgPSAhcmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKSAmJiAhcmVxdWlyZSgnLi9fZmFpbHMnKShmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBPYmplY3QuZGVmaW5lUHJvcGVydHkocmVxdWlyZSgnLi9fZG9tLWNyZWF0ZScpKCdkaXYnKSwgJ2EnLCB7IGdldDogZnVuY3Rpb24gKCkgeyByZXR1cm4gNzsgfSB9KS5hICE9IDc7XG59KTtcbiIsInZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xudmFyIHNldFByb3RvdHlwZU9mID0gcmVxdWlyZSgnLi9fc2V0LXByb3RvJykuc2V0O1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAodGhhdCwgdGFyZ2V0LCBDKSB7XG4gIHZhciBTID0gdGFyZ2V0LmNvbnN0cnVjdG9yO1xuICB2YXIgUDtcbiAgaWYgKFMgIT09IEMgJiYgdHlwZW9mIFMgPT0gJ2Z1bmN0aW9uJyAmJiAoUCA9IFMucHJvdG90eXBlKSAhPT0gQy5wcm90b3R5cGUgJiYgaXNPYmplY3QoUCkgJiYgc2V0UHJvdG90eXBlT2YpIHtcbiAgICBzZXRQcm90b3R5cGVPZih0aGF0LCBQKTtcbiAgfSByZXR1cm4gdGhhdDtcbn07XG4iLCIvLyBmYXN0IGFwcGx5LCBodHRwOi8vanNwZXJmLmxua2l0LmNvbS9mYXN0LWFwcGx5LzVcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGZuLCBhcmdzLCB0aGF0KSB7XG4gIHZhciB1biA9IHRoYXQgPT09IHVuZGVmaW5lZDtcbiAgc3dpdGNoIChhcmdzLmxlbmd0aCkge1xuICAgIGNhc2UgMDogcmV0dXJuIHVuID8gZm4oKVxuICAgICAgICAgICAgICAgICAgICAgIDogZm4uY2FsbCh0aGF0KTtcbiAgICBjYXNlIDE6IHJldHVybiB1biA/IGZuKGFyZ3NbMF0pXG4gICAgICAgICAgICAgICAgICAgICAgOiBmbi5jYWxsKHRoYXQsIGFyZ3NbMF0pO1xuICAgIGNhc2UgMjogcmV0dXJuIHVuID8gZm4oYXJnc1swXSwgYXJnc1sxXSlcbiAgICAgICAgICAgICAgICAgICAgICA6IGZuLmNhbGwodGhhdCwgYXJnc1swXSwgYXJnc1sxXSk7XG4gICAgY2FzZSAzOiByZXR1cm4gdW4gPyBmbihhcmdzWzBdLCBhcmdzWzFdLCBhcmdzWzJdKVxuICAgICAgICAgICAgICAgICAgICAgIDogZm4uY2FsbCh0aGF0LCBhcmdzWzBdLCBhcmdzWzFdLCBhcmdzWzJdKTtcbiAgICBjYXNlIDQ6IHJldHVybiB1biA/IGZuKGFyZ3NbMF0sIGFyZ3NbMV0sIGFyZ3NbMl0sIGFyZ3NbM10pXG4gICAgICAgICAgICAgICAgICAgICAgOiBmbi5jYWxsKHRoYXQsIGFyZ3NbMF0sIGFyZ3NbMV0sIGFyZ3NbMl0sIGFyZ3NbM10pO1xuICB9IHJldHVybiBmbi5hcHBseSh0aGF0LCBhcmdzKTtcbn07XG4iLCIvLyBmYWxsYmFjayBmb3Igbm9uLWFycmF5LWxpa2UgRVMzIGFuZCBub24tZW51bWVyYWJsZSBvbGQgVjggc3RyaW5nc1xudmFyIGNvZiA9IHJlcXVpcmUoJy4vX2NvZicpO1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXByb3RvdHlwZS1idWlsdGluc1xubW9kdWxlLmV4cG9ydHMgPSBPYmplY3QoJ3onKS5wcm9wZXJ0eUlzRW51bWVyYWJsZSgwKSA/IE9iamVjdCA6IGZ1bmN0aW9uIChpdCkge1xuICByZXR1cm4gY29mKGl0KSA9PSAnU3RyaW5nJyA/IGl0LnNwbGl0KCcnKSA6IE9iamVjdChpdCk7XG59O1xuIiwiLy8gY2hlY2sgb24gZGVmYXVsdCBBcnJheSBpdGVyYXRvclxudmFyIEl0ZXJhdG9ycyA9IHJlcXVpcmUoJy4vX2l0ZXJhdG9ycycpO1xudmFyIElURVJBVE9SID0gcmVxdWlyZSgnLi9fd2tzJykoJ2l0ZXJhdG9yJyk7XG52YXIgQXJyYXlQcm90byA9IEFycmF5LnByb3RvdHlwZTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaXQpIHtcbiAgcmV0dXJuIGl0ICE9PSB1bmRlZmluZWQgJiYgKEl0ZXJhdG9ycy5BcnJheSA9PT0gaXQgfHwgQXJyYXlQcm90b1tJVEVSQVRPUl0gPT09IGl0KTtcbn07XG4iLCIvLyA3LjIuMiBJc0FycmF5KGFyZ3VtZW50KVxudmFyIGNvZiA9IHJlcXVpcmUoJy4vX2NvZicpO1xubW9kdWxlLmV4cG9ydHMgPSBBcnJheS5pc0FycmF5IHx8IGZ1bmN0aW9uIGlzQXJyYXkoYXJnKSB7XG4gIHJldHVybiBjb2YoYXJnKSA9PSAnQXJyYXknO1xufTtcbiIsIi8vIDIwLjEuMi4zIE51bWJlci5pc0ludGVnZXIobnVtYmVyKVxudmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG52YXIgZmxvb3IgPSBNYXRoLmZsb29yO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiBpc0ludGVnZXIoaXQpIHtcbiAgcmV0dXJuICFpc09iamVjdChpdCkgJiYgaXNGaW5pdGUoaXQpICYmIGZsb29yKGl0KSA9PT0gaXQ7XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaXQpIHtcbiAgcmV0dXJuIHR5cGVvZiBpdCA9PT0gJ29iamVjdCcgPyBpdCAhPT0gbnVsbCA6IHR5cGVvZiBpdCA9PT0gJ2Z1bmN0aW9uJztcbn07XG4iLCIvLyA3LjIuOCBJc1JlZ0V4cChhcmd1bWVudClcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xudmFyIGNvZiA9IHJlcXVpcmUoJy4vX2NvZicpO1xudmFyIE1BVENIID0gcmVxdWlyZSgnLi9fd2tzJykoJ21hdGNoJyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCkge1xuICB2YXIgaXNSZWdFeHA7XG4gIHJldHVybiBpc09iamVjdChpdCkgJiYgKChpc1JlZ0V4cCA9IGl0W01BVENIXSkgIT09IHVuZGVmaW5lZCA/ICEhaXNSZWdFeHAgOiBjb2YoaXQpID09ICdSZWdFeHAnKTtcbn07XG4iLCIvLyBjYWxsIHNvbWV0aGluZyBvbiBpdGVyYXRvciBzdGVwIHdpdGggc2FmZSBjbG9zaW5nIG9uIGVycm9yXG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGl0ZXJhdG9yLCBmbiwgdmFsdWUsIGVudHJpZXMpIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4gZW50cmllcyA/IGZuKGFuT2JqZWN0KHZhbHVlKVswXSwgdmFsdWVbMV0pIDogZm4odmFsdWUpO1xuICAvLyA3LjQuNiBJdGVyYXRvckNsb3NlKGl0ZXJhdG9yLCBjb21wbGV0aW9uKVxuICB9IGNhdGNoIChlKSB7XG4gICAgdmFyIHJldCA9IGl0ZXJhdG9yWydyZXR1cm4nXTtcbiAgICBpZiAocmV0ICE9PSB1bmRlZmluZWQpIGFuT2JqZWN0KHJldC5jYWxsKGl0ZXJhdG9yKSk7XG4gICAgdGhyb3cgZTtcbiAgfVxufTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciBjcmVhdGUgPSByZXF1aXJlKCcuL19vYmplY3QtY3JlYXRlJyk7XG52YXIgZGVzY3JpcHRvciA9IHJlcXVpcmUoJy4vX3Byb3BlcnR5LWRlc2MnKTtcbnZhciBzZXRUb1N0cmluZ1RhZyA9IHJlcXVpcmUoJy4vX3NldC10by1zdHJpbmctdGFnJyk7XG52YXIgSXRlcmF0b3JQcm90b3R5cGUgPSB7fTtcblxuLy8gMjUuMS4yLjEuMSAlSXRlcmF0b3JQcm90b3R5cGUlW0BAaXRlcmF0b3JdKClcbnJlcXVpcmUoJy4vX2hpZGUnKShJdGVyYXRvclByb3RvdHlwZSwgcmVxdWlyZSgnLi9fd2tzJykoJ2l0ZXJhdG9yJyksIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXM7IH0pO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChDb25zdHJ1Y3RvciwgTkFNRSwgbmV4dCkge1xuICBDb25zdHJ1Y3Rvci5wcm90b3R5cGUgPSBjcmVhdGUoSXRlcmF0b3JQcm90b3R5cGUsIHsgbmV4dDogZGVzY3JpcHRvcigxLCBuZXh0KSB9KTtcbiAgc2V0VG9TdHJpbmdUYWcoQ29uc3RydWN0b3IsIE5BTUUgKyAnIEl0ZXJhdG9yJyk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIExJQlJBUlkgPSByZXF1aXJlKCcuL19saWJyYXJ5Jyk7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIHJlZGVmaW5lID0gcmVxdWlyZSgnLi9fcmVkZWZpbmUnKTtcbnZhciBoaWRlID0gcmVxdWlyZSgnLi9faGlkZScpO1xudmFyIEl0ZXJhdG9ycyA9IHJlcXVpcmUoJy4vX2l0ZXJhdG9ycycpO1xudmFyICRpdGVyQ3JlYXRlID0gcmVxdWlyZSgnLi9faXRlci1jcmVhdGUnKTtcbnZhciBzZXRUb1N0cmluZ1RhZyA9IHJlcXVpcmUoJy4vX3NldC10by1zdHJpbmctdGFnJyk7XG52YXIgZ2V0UHJvdG90eXBlT2YgPSByZXF1aXJlKCcuL19vYmplY3QtZ3BvJyk7XG52YXIgSVRFUkFUT1IgPSByZXF1aXJlKCcuL193a3MnKSgnaXRlcmF0b3InKTtcbnZhciBCVUdHWSA9ICEoW10ua2V5cyAmJiAnbmV4dCcgaW4gW10ua2V5cygpKTsgLy8gU2FmYXJpIGhhcyBidWdneSBpdGVyYXRvcnMgdy9vIGBuZXh0YFxudmFyIEZGX0lURVJBVE9SID0gJ0BAaXRlcmF0b3InO1xudmFyIEtFWVMgPSAna2V5cyc7XG52YXIgVkFMVUVTID0gJ3ZhbHVlcyc7XG5cbnZhciByZXR1cm5UaGlzID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoQmFzZSwgTkFNRSwgQ29uc3RydWN0b3IsIG5leHQsIERFRkFVTFQsIElTX1NFVCwgRk9SQ0VEKSB7XG4gICRpdGVyQ3JlYXRlKENvbnN0cnVjdG9yLCBOQU1FLCBuZXh0KTtcbiAgdmFyIGdldE1ldGhvZCA9IGZ1bmN0aW9uIChraW5kKSB7XG4gICAgaWYgKCFCVUdHWSAmJiBraW5kIGluIHByb3RvKSByZXR1cm4gcHJvdG9ba2luZF07XG4gICAgc3dpdGNoIChraW5kKSB7XG4gICAgICBjYXNlIEtFWVM6IHJldHVybiBmdW5jdGlvbiBrZXlzKCkgeyByZXR1cm4gbmV3IENvbnN0cnVjdG9yKHRoaXMsIGtpbmQpOyB9O1xuICAgICAgY2FzZSBWQUxVRVM6IHJldHVybiBmdW5jdGlvbiB2YWx1ZXMoKSB7IHJldHVybiBuZXcgQ29uc3RydWN0b3IodGhpcywga2luZCk7IH07XG4gICAgfSByZXR1cm4gZnVuY3Rpb24gZW50cmllcygpIHsgcmV0dXJuIG5ldyBDb25zdHJ1Y3Rvcih0aGlzLCBraW5kKTsgfTtcbiAgfTtcbiAgdmFyIFRBRyA9IE5BTUUgKyAnIEl0ZXJhdG9yJztcbiAgdmFyIERFRl9WQUxVRVMgPSBERUZBVUxUID09IFZBTFVFUztcbiAgdmFyIFZBTFVFU19CVUcgPSBmYWxzZTtcbiAgdmFyIHByb3RvID0gQmFzZS5wcm90b3R5cGU7XG4gIHZhciAkbmF0aXZlID0gcHJvdG9bSVRFUkFUT1JdIHx8IHByb3RvW0ZGX0lURVJBVE9SXSB8fCBERUZBVUxUICYmIHByb3RvW0RFRkFVTFRdO1xuICB2YXIgJGRlZmF1bHQgPSAkbmF0aXZlIHx8IGdldE1ldGhvZChERUZBVUxUKTtcbiAgdmFyICRlbnRyaWVzID0gREVGQVVMVCA/ICFERUZfVkFMVUVTID8gJGRlZmF1bHQgOiBnZXRNZXRob2QoJ2VudHJpZXMnKSA6IHVuZGVmaW5lZDtcbiAgdmFyICRhbnlOYXRpdmUgPSBOQU1FID09ICdBcnJheScgPyBwcm90by5lbnRyaWVzIHx8ICRuYXRpdmUgOiAkbmF0aXZlO1xuICB2YXIgbWV0aG9kcywga2V5LCBJdGVyYXRvclByb3RvdHlwZTtcbiAgLy8gRml4IG5hdGl2ZVxuICBpZiAoJGFueU5hdGl2ZSkge1xuICAgIEl0ZXJhdG9yUHJvdG90eXBlID0gZ2V0UHJvdG90eXBlT2YoJGFueU5hdGl2ZS5jYWxsKG5ldyBCYXNlKCkpKTtcbiAgICBpZiAoSXRlcmF0b3JQcm90b3R5cGUgIT09IE9iamVjdC5wcm90b3R5cGUgJiYgSXRlcmF0b3JQcm90b3R5cGUubmV4dCkge1xuICAgICAgLy8gU2V0IEBAdG9TdHJpbmdUYWcgdG8gbmF0aXZlIGl0ZXJhdG9yc1xuICAgICAgc2V0VG9TdHJpbmdUYWcoSXRlcmF0b3JQcm90b3R5cGUsIFRBRywgdHJ1ZSk7XG4gICAgICAvLyBmaXggZm9yIHNvbWUgb2xkIGVuZ2luZXNcbiAgICAgIGlmICghTElCUkFSWSAmJiB0eXBlb2YgSXRlcmF0b3JQcm90b3R5cGVbSVRFUkFUT1JdICE9ICdmdW5jdGlvbicpIGhpZGUoSXRlcmF0b3JQcm90b3R5cGUsIElURVJBVE9SLCByZXR1cm5UaGlzKTtcbiAgICB9XG4gIH1cbiAgLy8gZml4IEFycmF5I3t2YWx1ZXMsIEBAaXRlcmF0b3J9Lm5hbWUgaW4gVjggLyBGRlxuICBpZiAoREVGX1ZBTFVFUyAmJiAkbmF0aXZlICYmICRuYXRpdmUubmFtZSAhPT0gVkFMVUVTKSB7XG4gICAgVkFMVUVTX0JVRyA9IHRydWU7XG4gICAgJGRlZmF1bHQgPSBmdW5jdGlvbiB2YWx1ZXMoKSB7IHJldHVybiAkbmF0aXZlLmNhbGwodGhpcyk7IH07XG4gIH1cbiAgLy8gRGVmaW5lIGl0ZXJhdG9yXG4gIGlmICgoIUxJQlJBUlkgfHwgRk9SQ0VEKSAmJiAoQlVHR1kgfHwgVkFMVUVTX0JVRyB8fCAhcHJvdG9bSVRFUkFUT1JdKSkge1xuICAgIGhpZGUocHJvdG8sIElURVJBVE9SLCAkZGVmYXVsdCk7XG4gIH1cbiAgLy8gUGx1ZyBmb3IgbGlicmFyeVxuICBJdGVyYXRvcnNbTkFNRV0gPSAkZGVmYXVsdDtcbiAgSXRlcmF0b3JzW1RBR10gPSByZXR1cm5UaGlzO1xuICBpZiAoREVGQVVMVCkge1xuICAgIG1ldGhvZHMgPSB7XG4gICAgICB2YWx1ZXM6IERFRl9WQUxVRVMgPyAkZGVmYXVsdCA6IGdldE1ldGhvZChWQUxVRVMpLFxuICAgICAga2V5czogSVNfU0VUID8gJGRlZmF1bHQgOiBnZXRNZXRob2QoS0VZUyksXG4gICAgICBlbnRyaWVzOiAkZW50cmllc1xuICAgIH07XG4gICAgaWYgKEZPUkNFRCkgZm9yIChrZXkgaW4gbWV0aG9kcykge1xuICAgICAgaWYgKCEoa2V5IGluIHByb3RvKSkgcmVkZWZpbmUocHJvdG8sIGtleSwgbWV0aG9kc1trZXldKTtcbiAgICB9IGVsc2UgJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAoQlVHR1kgfHwgVkFMVUVTX0JVRyksIE5BTUUsIG1ldGhvZHMpO1xuICB9XG4gIHJldHVybiBtZXRob2RzO1xufTtcbiIsInZhciBJVEVSQVRPUiA9IHJlcXVpcmUoJy4vX3drcycpKCdpdGVyYXRvcicpO1xudmFyIFNBRkVfQ0xPU0lORyA9IGZhbHNlO1xuXG50cnkge1xuICB2YXIgcml0ZXIgPSBbN11bSVRFUkFUT1JdKCk7XG4gIHJpdGVyWydyZXR1cm4nXSA9IGZ1bmN0aW9uICgpIHsgU0FGRV9DTE9TSU5HID0gdHJ1ZTsgfTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXRocm93LWxpdGVyYWxcbiAgQXJyYXkuZnJvbShyaXRlciwgZnVuY3Rpb24gKCkgeyB0aHJvdyAyOyB9KTtcbn0gY2F0Y2ggKGUpIHsgLyogZW1wdHkgKi8gfVxuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChleGVjLCBza2lwQ2xvc2luZykge1xuICBpZiAoIXNraXBDbG9zaW5nICYmICFTQUZFX0NMT1NJTkcpIHJldHVybiBmYWxzZTtcbiAgdmFyIHNhZmUgPSBmYWxzZTtcbiAgdHJ5IHtcbiAgICB2YXIgYXJyID0gWzddO1xuICAgIHZhciBpdGVyID0gYXJyW0lURVJBVE9SXSgpO1xuICAgIGl0ZXIubmV4dCA9IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHsgZG9uZTogc2FmZSA9IHRydWUgfTsgfTtcbiAgICBhcnJbSVRFUkFUT1JdID0gZnVuY3Rpb24gKCkgeyByZXR1cm4gaXRlcjsgfTtcbiAgICBleGVjKGFycik7XG4gIH0gY2F0Y2ggKGUpIHsgLyogZW1wdHkgKi8gfVxuICByZXR1cm4gc2FmZTtcbn07XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChkb25lLCB2YWx1ZSkge1xuICByZXR1cm4geyB2YWx1ZTogdmFsdWUsIGRvbmU6ICEhZG9uZSB9O1xufTtcbiIsIm1vZHVsZS5leHBvcnRzID0ge307XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZhbHNlO1xuIiwiLy8gMjAuMi4yLjE0IE1hdGguZXhwbTEoeClcbnZhciAkZXhwbTEgPSBNYXRoLmV4cG0xO1xubW9kdWxlLmV4cG9ydHMgPSAoISRleHBtMVxuICAvLyBPbGQgRkYgYnVnXG4gIHx8ICRleHBtMSgxMCkgPiAyMjAyNS40NjU3OTQ4MDY3MTkgfHwgJGV4cG0xKDEwKSA8IDIyMDI1LjQ2NTc5NDgwNjcxNjUxNjhcbiAgLy8gVG9yIEJyb3dzZXIgYnVnXG4gIHx8ICRleHBtMSgtMmUtMTcpICE9IC0yZS0xN1xuKSA/IGZ1bmN0aW9uIGV4cG0xKHgpIHtcbiAgcmV0dXJuICh4ID0gK3gpID09IDAgPyB4IDogeCA+IC0xZS02ICYmIHggPCAxZS02ID8geCArIHggKiB4IC8gMiA6IE1hdGguZXhwKHgpIC0gMTtcbn0gOiAkZXhwbTE7XG4iLCIvLyAyMC4yLjIuMTYgTWF0aC5mcm91bmQoeClcbnZhciBzaWduID0gcmVxdWlyZSgnLi9fbWF0aC1zaWduJyk7XG52YXIgcG93ID0gTWF0aC5wb3c7XG52YXIgRVBTSUxPTiA9IHBvdygyLCAtNTIpO1xudmFyIEVQU0lMT04zMiA9IHBvdygyLCAtMjMpO1xudmFyIE1BWDMyID0gcG93KDIsIDEyNykgKiAoMiAtIEVQU0lMT04zMik7XG52YXIgTUlOMzIgPSBwb3coMiwgLTEyNik7XG5cbnZhciByb3VuZFRpZXNUb0V2ZW4gPSBmdW5jdGlvbiAobikge1xuICByZXR1cm4gbiArIDEgLyBFUFNJTE9OIC0gMSAvIEVQU0lMT047XG59O1xuXG5tb2R1bGUuZXhwb3J0cyA9IE1hdGguZnJvdW5kIHx8IGZ1bmN0aW9uIGZyb3VuZCh4KSB7XG4gIHZhciAkYWJzID0gTWF0aC5hYnMoeCk7XG4gIHZhciAkc2lnbiA9IHNpZ24oeCk7XG4gIHZhciBhLCByZXN1bHQ7XG4gIGlmICgkYWJzIDwgTUlOMzIpIHJldHVybiAkc2lnbiAqIHJvdW5kVGllc1RvRXZlbigkYWJzIC8gTUlOMzIgLyBFUFNJTE9OMzIpICogTUlOMzIgKiBFUFNJTE9OMzI7XG4gIGEgPSAoMSArIEVQU0lMT04zMiAvIEVQU0lMT04pICogJGFicztcbiAgcmVzdWx0ID0gYSAtIChhIC0gJGFicyk7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1zZWxmLWNvbXBhcmVcbiAgaWYgKHJlc3VsdCA+IE1BWDMyIHx8IHJlc3VsdCAhPSByZXN1bHQpIHJldHVybiAkc2lnbiAqIEluZmluaXR5O1xuICByZXR1cm4gJHNpZ24gKiByZXN1bHQ7XG59O1xuIiwiLy8gMjAuMi4yLjIwIE1hdGgubG9nMXAoeClcbm1vZHVsZS5leHBvcnRzID0gTWF0aC5sb2cxcCB8fCBmdW5jdGlvbiBsb2cxcCh4KSB7XG4gIHJldHVybiAoeCA9ICt4KSA+IC0xZS04ICYmIHggPCAxZS04ID8geCAtIHggKiB4IC8gMiA6IE1hdGgubG9nKDEgKyB4KTtcbn07XG4iLCIvLyBodHRwczovL3J3YWxkcm9uLmdpdGh1Yi5pby9wcm9wb3NhbC1tYXRoLWV4dGVuc2lvbnMvXG5tb2R1bGUuZXhwb3J0cyA9IE1hdGguc2NhbGUgfHwgZnVuY3Rpb24gc2NhbGUoeCwgaW5Mb3csIGluSGlnaCwgb3V0TG93LCBvdXRIaWdoKSB7XG4gIGlmIChcbiAgICBhcmd1bWVudHMubGVuZ3RoID09PSAwXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tc2VsZi1jb21wYXJlXG4gICAgICB8fCB4ICE9IHhcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1zZWxmLWNvbXBhcmVcbiAgICAgIHx8IGluTG93ICE9IGluTG93XG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tc2VsZi1jb21wYXJlXG4gICAgICB8fCBpbkhpZ2ggIT0gaW5IaWdoXG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tc2VsZi1jb21wYXJlXG4gICAgICB8fCBvdXRMb3cgIT0gb3V0TG93XG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tc2VsZi1jb21wYXJlXG4gICAgICB8fCBvdXRIaWdoICE9IG91dEhpZ2hcbiAgKSByZXR1cm4gTmFOO1xuICBpZiAoeCA9PT0gSW5maW5pdHkgfHwgeCA9PT0gLUluZmluaXR5KSByZXR1cm4geDtcbiAgcmV0dXJuICh4IC0gaW5Mb3cpICogKG91dEhpZ2ggLSBvdXRMb3cpIC8gKGluSGlnaCAtIGluTG93KSArIG91dExvdztcbn07XG4iLCIvLyAyMC4yLjIuMjggTWF0aC5zaWduKHgpXG5tb2R1bGUuZXhwb3J0cyA9IE1hdGguc2lnbiB8fCBmdW5jdGlvbiBzaWduKHgpIHtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXNlbGYtY29tcGFyZVxuICByZXR1cm4gKHggPSAreCkgPT0gMCB8fCB4ICE9IHggPyB4IDogeCA8IDAgPyAtMSA6IDE7XG59O1xuIiwidmFyIE1FVEEgPSByZXF1aXJlKCcuL191aWQnKSgnbWV0YScpO1xudmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG52YXIgaGFzID0gcmVxdWlyZSgnLi9faGFzJyk7XG52YXIgc2V0RGVzYyA9IHJlcXVpcmUoJy4vX29iamVjdC1kcCcpLmY7XG52YXIgaWQgPSAwO1xudmFyIGlzRXh0ZW5zaWJsZSA9IE9iamVjdC5pc0V4dGVuc2libGUgfHwgZnVuY3Rpb24gKCkge1xuICByZXR1cm4gdHJ1ZTtcbn07XG52YXIgRlJFRVpFID0gIXJlcXVpcmUoJy4vX2ZhaWxzJykoZnVuY3Rpb24gKCkge1xuICByZXR1cm4gaXNFeHRlbnNpYmxlKE9iamVjdC5wcmV2ZW50RXh0ZW5zaW9ucyh7fSkpO1xufSk7XG52YXIgc2V0TWV0YSA9IGZ1bmN0aW9uIChpdCkge1xuICBzZXREZXNjKGl0LCBNRVRBLCB7IHZhbHVlOiB7XG4gICAgaTogJ08nICsgKytpZCwgLy8gb2JqZWN0IElEXG4gICAgdzoge30gICAgICAgICAgLy8gd2VhayBjb2xsZWN0aW9ucyBJRHNcbiAgfSB9KTtcbn07XG52YXIgZmFzdEtleSA9IGZ1bmN0aW9uIChpdCwgY3JlYXRlKSB7XG4gIC8vIHJldHVybiBwcmltaXRpdmUgd2l0aCBwcmVmaXhcbiAgaWYgKCFpc09iamVjdChpdCkpIHJldHVybiB0eXBlb2YgaXQgPT0gJ3N5bWJvbCcgPyBpdCA6ICh0eXBlb2YgaXQgPT0gJ3N0cmluZycgPyAnUycgOiAnUCcpICsgaXQ7XG4gIGlmICghaGFzKGl0LCBNRVRBKSkge1xuICAgIC8vIGNhbid0IHNldCBtZXRhZGF0YSB0byB1bmNhdWdodCBmcm96ZW4gb2JqZWN0XG4gICAgaWYgKCFpc0V4dGVuc2libGUoaXQpKSByZXR1cm4gJ0YnO1xuICAgIC8vIG5vdCBuZWNlc3NhcnkgdG8gYWRkIG1ldGFkYXRhXG4gICAgaWYgKCFjcmVhdGUpIHJldHVybiAnRSc7XG4gICAgLy8gYWRkIG1pc3NpbmcgbWV0YWRhdGFcbiAgICBzZXRNZXRhKGl0KTtcbiAgLy8gcmV0dXJuIG9iamVjdCBJRFxuICB9IHJldHVybiBpdFtNRVRBXS5pO1xufTtcbnZhciBnZXRXZWFrID0gZnVuY3Rpb24gKGl0LCBjcmVhdGUpIHtcbiAgaWYgKCFoYXMoaXQsIE1FVEEpKSB7XG4gICAgLy8gY2FuJ3Qgc2V0IG1ldGFkYXRhIHRvIHVuY2F1Z2h0IGZyb3plbiBvYmplY3RcbiAgICBpZiAoIWlzRXh0ZW5zaWJsZShpdCkpIHJldHVybiB0cnVlO1xuICAgIC8vIG5vdCBuZWNlc3NhcnkgdG8gYWRkIG1ldGFkYXRhXG4gICAgaWYgKCFjcmVhdGUpIHJldHVybiBmYWxzZTtcbiAgICAvLyBhZGQgbWlzc2luZyBtZXRhZGF0YVxuICAgIHNldE1ldGEoaXQpO1xuICAvLyByZXR1cm4gaGFzaCB3ZWFrIGNvbGxlY3Rpb25zIElEc1xuICB9IHJldHVybiBpdFtNRVRBXS53O1xufTtcbi8vIGFkZCBtZXRhZGF0YSBvbiBmcmVlemUtZmFtaWx5IG1ldGhvZHMgY2FsbGluZ1xudmFyIG9uRnJlZXplID0gZnVuY3Rpb24gKGl0KSB7XG4gIGlmIChGUkVFWkUgJiYgbWV0YS5ORUVEICYmIGlzRXh0ZW5zaWJsZShpdCkgJiYgIWhhcyhpdCwgTUVUQSkpIHNldE1ldGEoaXQpO1xuICByZXR1cm4gaXQ7XG59O1xudmFyIG1ldGEgPSBtb2R1bGUuZXhwb3J0cyA9IHtcbiAgS0VZOiBNRVRBLFxuICBORUVEOiBmYWxzZSxcbiAgZmFzdEtleTogZmFzdEtleSxcbiAgZ2V0V2VhazogZ2V0V2VhayxcbiAgb25GcmVlemU6IG9uRnJlZXplXG59O1xuIiwidmFyIE1hcCA9IHJlcXVpcmUoJy4vZXM2Lm1hcCcpO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBzaGFyZWQgPSByZXF1aXJlKCcuL19zaGFyZWQnKSgnbWV0YWRhdGEnKTtcbnZhciBzdG9yZSA9IHNoYXJlZC5zdG9yZSB8fCAoc2hhcmVkLnN0b3JlID0gbmV3IChyZXF1aXJlKCcuL2VzNi53ZWFrLW1hcCcpKSgpKTtcblxudmFyIGdldE9yQ3JlYXRlTWV0YWRhdGFNYXAgPSBmdW5jdGlvbiAodGFyZ2V0LCB0YXJnZXRLZXksIGNyZWF0ZSkge1xuICB2YXIgdGFyZ2V0TWV0YWRhdGEgPSBzdG9yZS5nZXQodGFyZ2V0KTtcbiAgaWYgKCF0YXJnZXRNZXRhZGF0YSkge1xuICAgIGlmICghY3JlYXRlKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgIHN0b3JlLnNldCh0YXJnZXQsIHRhcmdldE1ldGFkYXRhID0gbmV3IE1hcCgpKTtcbiAgfVxuICB2YXIga2V5TWV0YWRhdGEgPSB0YXJnZXRNZXRhZGF0YS5nZXQodGFyZ2V0S2V5KTtcbiAgaWYgKCFrZXlNZXRhZGF0YSkge1xuICAgIGlmICghY3JlYXRlKSByZXR1cm4gdW5kZWZpbmVkO1xuICAgIHRhcmdldE1ldGFkYXRhLnNldCh0YXJnZXRLZXksIGtleU1ldGFkYXRhID0gbmV3IE1hcCgpKTtcbiAgfSByZXR1cm4ga2V5TWV0YWRhdGE7XG59O1xudmFyIG9yZGluYXJ5SGFzT3duTWV0YWRhdGEgPSBmdW5jdGlvbiAoTWV0YWRhdGFLZXksIE8sIFApIHtcbiAgdmFyIG1ldGFkYXRhTWFwID0gZ2V0T3JDcmVhdGVNZXRhZGF0YU1hcChPLCBQLCBmYWxzZSk7XG4gIHJldHVybiBtZXRhZGF0YU1hcCA9PT0gdW5kZWZpbmVkID8gZmFsc2UgOiBtZXRhZGF0YU1hcC5oYXMoTWV0YWRhdGFLZXkpO1xufTtcbnZhciBvcmRpbmFyeUdldE93bk1ldGFkYXRhID0gZnVuY3Rpb24gKE1ldGFkYXRhS2V5LCBPLCBQKSB7XG4gIHZhciBtZXRhZGF0YU1hcCA9IGdldE9yQ3JlYXRlTWV0YWRhdGFNYXAoTywgUCwgZmFsc2UpO1xuICByZXR1cm4gbWV0YWRhdGFNYXAgPT09IHVuZGVmaW5lZCA/IHVuZGVmaW5lZCA6IG1ldGFkYXRhTWFwLmdldChNZXRhZGF0YUtleSk7XG59O1xudmFyIG9yZGluYXJ5RGVmaW5lT3duTWV0YWRhdGEgPSBmdW5jdGlvbiAoTWV0YWRhdGFLZXksIE1ldGFkYXRhVmFsdWUsIE8sIFApIHtcbiAgZ2V0T3JDcmVhdGVNZXRhZGF0YU1hcChPLCBQLCB0cnVlKS5zZXQoTWV0YWRhdGFLZXksIE1ldGFkYXRhVmFsdWUpO1xufTtcbnZhciBvcmRpbmFyeU93bk1ldGFkYXRhS2V5cyA9IGZ1bmN0aW9uICh0YXJnZXQsIHRhcmdldEtleSkge1xuICB2YXIgbWV0YWRhdGFNYXAgPSBnZXRPckNyZWF0ZU1ldGFkYXRhTWFwKHRhcmdldCwgdGFyZ2V0S2V5LCBmYWxzZSk7XG4gIHZhciBrZXlzID0gW107XG4gIGlmIChtZXRhZGF0YU1hcCkgbWV0YWRhdGFNYXAuZm9yRWFjaChmdW5jdGlvbiAoXywga2V5KSB7IGtleXMucHVzaChrZXkpOyB9KTtcbiAgcmV0dXJuIGtleXM7XG59O1xudmFyIHRvTWV0YUtleSA9IGZ1bmN0aW9uIChpdCkge1xuICByZXR1cm4gaXQgPT09IHVuZGVmaW5lZCB8fCB0eXBlb2YgaXQgPT0gJ3N5bWJvbCcgPyBpdCA6IFN0cmluZyhpdCk7XG59O1xudmFyIGV4cCA9IGZ1bmN0aW9uIChPKSB7XG4gICRleHBvcnQoJGV4cG9ydC5TLCAnUmVmbGVjdCcsIE8pO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHN0b3JlOiBzdG9yZSxcbiAgbWFwOiBnZXRPckNyZWF0ZU1ldGFkYXRhTWFwLFxuICBoYXM6IG9yZGluYXJ5SGFzT3duTWV0YWRhdGEsXG4gIGdldDogb3JkaW5hcnlHZXRPd25NZXRhZGF0YSxcbiAgc2V0OiBvcmRpbmFyeURlZmluZU93bk1ldGFkYXRhLFxuICBrZXlzOiBvcmRpbmFyeU93bk1ldGFkYXRhS2V5cyxcbiAga2V5OiB0b01ldGFLZXksXG4gIGV4cDogZXhwXG59O1xuIiwidmFyIGdsb2JhbCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpO1xudmFyIG1hY3JvdGFzayA9IHJlcXVpcmUoJy4vX3Rhc2snKS5zZXQ7XG52YXIgT2JzZXJ2ZXIgPSBnbG9iYWwuTXV0YXRpb25PYnNlcnZlciB8fCBnbG9iYWwuV2ViS2l0TXV0YXRpb25PYnNlcnZlcjtcbnZhciBwcm9jZXNzID0gZ2xvYmFsLnByb2Nlc3M7XG52YXIgUHJvbWlzZSA9IGdsb2JhbC5Qcm9taXNlO1xudmFyIGlzTm9kZSA9IHJlcXVpcmUoJy4vX2NvZicpKHByb2Nlc3MpID09ICdwcm9jZXNzJztcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoKSB7XG4gIHZhciBoZWFkLCBsYXN0LCBub3RpZnk7XG5cbiAgdmFyIGZsdXNoID0gZnVuY3Rpb24gKCkge1xuICAgIHZhciBwYXJlbnQsIGZuO1xuICAgIGlmIChpc05vZGUgJiYgKHBhcmVudCA9IHByb2Nlc3MuZG9tYWluKSkgcGFyZW50LmV4aXQoKTtcbiAgICB3aGlsZSAoaGVhZCkge1xuICAgICAgZm4gPSBoZWFkLmZuO1xuICAgICAgaGVhZCA9IGhlYWQubmV4dDtcbiAgICAgIHRyeSB7XG4gICAgICAgIGZuKCk7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIGlmIChoZWFkKSBub3RpZnkoKTtcbiAgICAgICAgZWxzZSBsYXN0ID0gdW5kZWZpbmVkO1xuICAgICAgICB0aHJvdyBlO1xuICAgICAgfVxuICAgIH0gbGFzdCA9IHVuZGVmaW5lZDtcbiAgICBpZiAocGFyZW50KSBwYXJlbnQuZW50ZXIoKTtcbiAgfTtcblxuICAvLyBOb2RlLmpzXG4gIGlmIChpc05vZGUpIHtcbiAgICBub3RpZnkgPSBmdW5jdGlvbiAoKSB7XG4gICAgICBwcm9jZXNzLm5leHRUaWNrKGZsdXNoKTtcbiAgICB9O1xuICAvLyBicm93c2VycyB3aXRoIE11dGF0aW9uT2JzZXJ2ZXIsIGV4Y2VwdCBpT1MgU2FmYXJpIC0gaHR0cHM6Ly9naXRodWIuY29tL3psb2lyb2NrL2NvcmUtanMvaXNzdWVzLzMzOVxuICB9IGVsc2UgaWYgKE9ic2VydmVyICYmICEoZ2xvYmFsLm5hdmlnYXRvciAmJiBnbG9iYWwubmF2aWdhdG9yLnN0YW5kYWxvbmUpKSB7XG4gICAgdmFyIHRvZ2dsZSA9IHRydWU7XG4gICAgdmFyIG5vZGUgPSBkb2N1bWVudC5jcmVhdGVUZXh0Tm9kZSgnJyk7XG4gICAgbmV3IE9ic2VydmVyKGZsdXNoKS5vYnNlcnZlKG5vZGUsIHsgY2hhcmFjdGVyRGF0YTogdHJ1ZSB9KTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1uZXdcbiAgICBub3RpZnkgPSBmdW5jdGlvbiAoKSB7XG4gICAgICBub2RlLmRhdGEgPSB0b2dnbGUgPSAhdG9nZ2xlO1xuICAgIH07XG4gIC8vIGVudmlyb25tZW50cyB3aXRoIG1heWJlIG5vbi1jb21wbGV0ZWx5IGNvcnJlY3QsIGJ1dCBleGlzdGVudCBQcm9taXNlXG4gIH0gZWxzZSBpZiAoUHJvbWlzZSAmJiBQcm9taXNlLnJlc29sdmUpIHtcbiAgICAvLyBQcm9taXNlLnJlc29sdmUgd2l0aG91dCBhbiBhcmd1bWVudCB0aHJvd3MgYW4gZXJyb3IgaW4gTEcgV2ViT1MgMlxuICAgIHZhciBwcm9taXNlID0gUHJvbWlzZS5yZXNvbHZlKHVuZGVmaW5lZCk7XG4gICAgbm90aWZ5ID0gZnVuY3Rpb24gKCkge1xuICAgICAgcHJvbWlzZS50aGVuKGZsdXNoKTtcbiAgICB9O1xuICAvLyBmb3Igb3RoZXIgZW52aXJvbm1lbnRzIC0gbWFjcm90YXNrIGJhc2VkIG9uOlxuICAvLyAtIHNldEltbWVkaWF0ZVxuICAvLyAtIE1lc3NhZ2VDaGFubmVsXG4gIC8vIC0gd2luZG93LnBvc3RNZXNzYWdcbiAgLy8gLSBvbnJlYWR5c3RhdGVjaGFuZ2VcbiAgLy8gLSBzZXRUaW1lb3V0XG4gIH0gZWxzZSB7XG4gICAgbm90aWZ5ID0gZnVuY3Rpb24gKCkge1xuICAgICAgLy8gc3RyYW5nZSBJRSArIHdlYnBhY2sgZGV2IHNlcnZlciBidWcgLSB1c2UgLmNhbGwoZ2xvYmFsKVxuICAgICAgbWFjcm90YXNrLmNhbGwoZ2xvYmFsLCBmbHVzaCk7XG4gICAgfTtcbiAgfVxuXG4gIHJldHVybiBmdW5jdGlvbiAoZm4pIHtcbiAgICB2YXIgdGFzayA9IHsgZm46IGZuLCBuZXh0OiB1bmRlZmluZWQgfTtcbiAgICBpZiAobGFzdCkgbGFzdC5uZXh0ID0gdGFzaztcbiAgICBpZiAoIWhlYWQpIHtcbiAgICAgIGhlYWQgPSB0YXNrO1xuICAgICAgbm90aWZ5KCk7XG4gICAgfSBsYXN0ID0gdGFzaztcbiAgfTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG4vLyAyNS40LjEuNSBOZXdQcm9taXNlQ2FwYWJpbGl0eShDKVxudmFyIGFGdW5jdGlvbiA9IHJlcXVpcmUoJy4vX2EtZnVuY3Rpb24nKTtcblxuZnVuY3Rpb24gUHJvbWlzZUNhcGFiaWxpdHkoQykge1xuICB2YXIgcmVzb2x2ZSwgcmVqZWN0O1xuICB0aGlzLnByb21pc2UgPSBuZXcgQyhmdW5jdGlvbiAoJCRyZXNvbHZlLCAkJHJlamVjdCkge1xuICAgIGlmIChyZXNvbHZlICE9PSB1bmRlZmluZWQgfHwgcmVqZWN0ICE9PSB1bmRlZmluZWQpIHRocm93IFR5cGVFcnJvcignQmFkIFByb21pc2UgY29uc3RydWN0b3InKTtcbiAgICByZXNvbHZlID0gJCRyZXNvbHZlO1xuICAgIHJlamVjdCA9ICQkcmVqZWN0O1xuICB9KTtcbiAgdGhpcy5yZXNvbHZlID0gYUZ1bmN0aW9uKHJlc29sdmUpO1xuICB0aGlzLnJlamVjdCA9IGFGdW5jdGlvbihyZWplY3QpO1xufVxuXG5tb2R1bGUuZXhwb3J0cy5mID0gZnVuY3Rpb24gKEMpIHtcbiAgcmV0dXJuIG5ldyBQcm9taXNlQ2FwYWJpbGl0eShDKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG4vLyAxOS4xLjIuMSBPYmplY3QuYXNzaWduKHRhcmdldCwgc291cmNlLCAuLi4pXG52YXIgZ2V0S2V5cyA9IHJlcXVpcmUoJy4vX29iamVjdC1rZXlzJyk7XG52YXIgZ09QUyA9IHJlcXVpcmUoJy4vX29iamVjdC1nb3BzJyk7XG52YXIgcElFID0gcmVxdWlyZSgnLi9fb2JqZWN0LXBpZScpO1xudmFyIHRvT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8tb2JqZWN0Jyk7XG52YXIgSU9iamVjdCA9IHJlcXVpcmUoJy4vX2lvYmplY3QnKTtcbnZhciAkYXNzaWduID0gT2JqZWN0LmFzc2lnbjtcblxuLy8gc2hvdWxkIHdvcmsgd2l0aCBzeW1ib2xzIGFuZCBzaG91bGQgaGF2ZSBkZXRlcm1pbmlzdGljIHByb3BlcnR5IG9yZGVyIChWOCBidWcpXG5tb2R1bGUuZXhwb3J0cyA9ICEkYXNzaWduIHx8IHJlcXVpcmUoJy4vX2ZhaWxzJykoZnVuY3Rpb24gKCkge1xuICB2YXIgQSA9IHt9O1xuICB2YXIgQiA9IHt9O1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcbiAgdmFyIFMgPSBTeW1ib2woKTtcbiAgdmFyIEsgPSAnYWJjZGVmZ2hpamtsbW5vcHFyc3QnO1xuICBBW1NdID0gNztcbiAgSy5zcGxpdCgnJykuZm9yRWFjaChmdW5jdGlvbiAoaykgeyBCW2tdID0gazsgfSk7XG4gIHJldHVybiAkYXNzaWduKHt9LCBBKVtTXSAhPSA3IHx8IE9iamVjdC5rZXlzKCRhc3NpZ24oe30sIEIpKS5qb2luKCcnKSAhPSBLO1xufSkgPyBmdW5jdGlvbiBhc3NpZ24odGFyZ2V0LCBzb3VyY2UpIHsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby11bnVzZWQtdmFyc1xuICB2YXIgVCA9IHRvT2JqZWN0KHRhcmdldCk7XG4gIHZhciBhTGVuID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgdmFyIGluZGV4ID0gMTtcbiAgdmFyIGdldFN5bWJvbHMgPSBnT1BTLmY7XG4gIHZhciBpc0VudW0gPSBwSUUuZjtcbiAgd2hpbGUgKGFMZW4gPiBpbmRleCkge1xuICAgIHZhciBTID0gSU9iamVjdChhcmd1bWVudHNbaW5kZXgrK10pO1xuICAgIHZhciBrZXlzID0gZ2V0U3ltYm9scyA/IGdldEtleXMoUykuY29uY2F0KGdldFN5bWJvbHMoUykpIDogZ2V0S2V5cyhTKTtcbiAgICB2YXIgbGVuZ3RoID0ga2V5cy5sZW5ndGg7XG4gICAgdmFyIGogPSAwO1xuICAgIHZhciBrZXk7XG4gICAgd2hpbGUgKGxlbmd0aCA+IGopIGlmIChpc0VudW0uY2FsbChTLCBrZXkgPSBrZXlzW2orK10pKSBUW2tleV0gPSBTW2tleV07XG4gIH0gcmV0dXJuIFQ7XG59IDogJGFzc2lnbjtcbiIsIi8vIDE5LjEuMi4yIC8gMTUuMi4zLjUgT2JqZWN0LmNyZWF0ZShPIFssIFByb3BlcnRpZXNdKVxudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgZFBzID0gcmVxdWlyZSgnLi9fb2JqZWN0LWRwcycpO1xudmFyIGVudW1CdWdLZXlzID0gcmVxdWlyZSgnLi9fZW51bS1idWcta2V5cycpO1xudmFyIElFX1BST1RPID0gcmVxdWlyZSgnLi9fc2hhcmVkLWtleScpKCdJRV9QUk9UTycpO1xudmFyIEVtcHR5ID0gZnVuY3Rpb24gKCkgeyAvKiBlbXB0eSAqLyB9O1xudmFyIFBST1RPVFlQRSA9ICdwcm90b3R5cGUnO1xuXG4vLyBDcmVhdGUgb2JqZWN0IHdpdGggZmFrZSBgbnVsbGAgcHJvdG90eXBlOiB1c2UgaWZyYW1lIE9iamVjdCB3aXRoIGNsZWFyZWQgcHJvdG90eXBlXG52YXIgY3JlYXRlRGljdCA9IGZ1bmN0aW9uICgpIHtcbiAgLy8gVGhyYXNoLCB3YXN0ZSBhbmQgc29kb215OiBJRSBHQyBidWdcbiAgdmFyIGlmcmFtZSA9IHJlcXVpcmUoJy4vX2RvbS1jcmVhdGUnKSgnaWZyYW1lJyk7XG4gIHZhciBpID0gZW51bUJ1Z0tleXMubGVuZ3RoO1xuICB2YXIgbHQgPSAnPCc7XG4gIHZhciBndCA9ICc+JztcbiAgdmFyIGlmcmFtZURvY3VtZW50O1xuICBpZnJhbWUuc3R5bGUuZGlzcGxheSA9ICdub25lJztcbiAgcmVxdWlyZSgnLi9faHRtbCcpLmFwcGVuZENoaWxkKGlmcmFtZSk7XG4gIGlmcmFtZS5zcmMgPSAnamF2YXNjcmlwdDonOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXNjcmlwdC11cmxcbiAgLy8gY3JlYXRlRGljdCA9IGlmcmFtZS5jb250ZW50V2luZG93Lk9iamVjdDtcbiAgLy8gaHRtbC5yZW1vdmVDaGlsZChpZnJhbWUpO1xuICBpZnJhbWVEb2N1bWVudCA9IGlmcmFtZS5jb250ZW50V2luZG93LmRvY3VtZW50O1xuICBpZnJhbWVEb2N1bWVudC5vcGVuKCk7XG4gIGlmcmFtZURvY3VtZW50LndyaXRlKGx0ICsgJ3NjcmlwdCcgKyBndCArICdkb2N1bWVudC5GPU9iamVjdCcgKyBsdCArICcvc2NyaXB0JyArIGd0KTtcbiAgaWZyYW1lRG9jdW1lbnQuY2xvc2UoKTtcbiAgY3JlYXRlRGljdCA9IGlmcmFtZURvY3VtZW50LkY7XG4gIHdoaWxlIChpLS0pIGRlbGV0ZSBjcmVhdGVEaWN0W1BST1RPVFlQRV1bZW51bUJ1Z0tleXNbaV1dO1xuICByZXR1cm4gY3JlYXRlRGljdCgpO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBPYmplY3QuY3JlYXRlIHx8IGZ1bmN0aW9uIGNyZWF0ZShPLCBQcm9wZXJ0aWVzKSB7XG4gIHZhciByZXN1bHQ7XG4gIGlmIChPICE9PSBudWxsKSB7XG4gICAgRW1wdHlbUFJPVE9UWVBFXSA9IGFuT2JqZWN0KE8pO1xuICAgIHJlc3VsdCA9IG5ldyBFbXB0eSgpO1xuICAgIEVtcHR5W1BST1RPVFlQRV0gPSBudWxsO1xuICAgIC8vIGFkZCBcIl9fcHJvdG9fX1wiIGZvciBPYmplY3QuZ2V0UHJvdG90eXBlT2YgcG9seWZpbGxcbiAgICByZXN1bHRbSUVfUFJPVE9dID0gTztcbiAgfSBlbHNlIHJlc3VsdCA9IGNyZWF0ZURpY3QoKTtcbiAgcmV0dXJuIFByb3BlcnRpZXMgPT09IHVuZGVmaW5lZCA/IHJlc3VsdCA6IGRQcyhyZXN1bHQsIFByb3BlcnRpZXMpO1xufTtcbiIsInZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIElFOF9ET01fREVGSU5FID0gcmVxdWlyZSgnLi9faWU4LWRvbS1kZWZpbmUnKTtcbnZhciB0b1ByaW1pdGl2ZSA9IHJlcXVpcmUoJy4vX3RvLXByaW1pdGl2ZScpO1xudmFyIGRQID0gT2JqZWN0LmRlZmluZVByb3BlcnR5O1xuXG5leHBvcnRzLmYgPSByZXF1aXJlKCcuL19kZXNjcmlwdG9ycycpID8gT2JqZWN0LmRlZmluZVByb3BlcnR5IDogZnVuY3Rpb24gZGVmaW5lUHJvcGVydHkoTywgUCwgQXR0cmlidXRlcykge1xuICBhbk9iamVjdChPKTtcbiAgUCA9IHRvUHJpbWl0aXZlKFAsIHRydWUpO1xuICBhbk9iamVjdChBdHRyaWJ1dGVzKTtcbiAgaWYgKElFOF9ET01fREVGSU5FKSB0cnkge1xuICAgIHJldHVybiBkUChPLCBQLCBBdHRyaWJ1dGVzKTtcbiAgfSBjYXRjaCAoZSkgeyAvKiBlbXB0eSAqLyB9XG4gIGlmICgnZ2V0JyBpbiBBdHRyaWJ1dGVzIHx8ICdzZXQnIGluIEF0dHJpYnV0ZXMpIHRocm93IFR5cGVFcnJvcignQWNjZXNzb3JzIG5vdCBzdXBwb3J0ZWQhJyk7XG4gIGlmICgndmFsdWUnIGluIEF0dHJpYnV0ZXMpIE9bUF0gPSBBdHRyaWJ1dGVzLnZhbHVlO1xuICByZXR1cm4gTztcbn07XG4iLCJ2YXIgZFAgPSByZXF1aXJlKCcuL19vYmplY3QtZHAnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIGdldEtleXMgPSByZXF1aXJlKCcuL19vYmplY3Qta2V5cycpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoJy4vX2Rlc2NyaXB0b3JzJykgPyBPYmplY3QuZGVmaW5lUHJvcGVydGllcyA6IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXMoTywgUHJvcGVydGllcykge1xuICBhbk9iamVjdChPKTtcbiAgdmFyIGtleXMgPSBnZXRLZXlzKFByb3BlcnRpZXMpO1xuICB2YXIgbGVuZ3RoID0ga2V5cy5sZW5ndGg7XG4gIHZhciBpID0gMDtcbiAgdmFyIFA7XG4gIHdoaWxlIChsZW5ndGggPiBpKSBkUC5mKE8sIFAgPSBrZXlzW2krK10sIFByb3BlcnRpZXNbUF0pO1xuICByZXR1cm4gTztcbn07XG4iLCIndXNlIHN0cmljdCc7XG4vLyBGb3JjZWQgcmVwbGFjZW1lbnQgcHJvdG90eXBlIGFjY2Vzc29ycyBtZXRob2RzXG5tb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoJy4vX2xpYnJhcnknKSB8fCAhcmVxdWlyZSgnLi9fZmFpbHMnKShmdW5jdGlvbiAoKSB7XG4gIHZhciBLID0gTWF0aC5yYW5kb20oKTtcbiAgLy8gSW4gRkYgdGhyb3dzIG9ubHkgZGVmaW5lIG1ldGhvZHNcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVuZGVmLCBuby11c2VsZXNzLWNhbGxcbiAgX19kZWZpbmVTZXR0ZXJfXy5jYWxsKG51bGwsIEssIGZ1bmN0aW9uICgpIHsgLyogZW1wdHkgKi8gfSk7XG4gIGRlbGV0ZSByZXF1aXJlKCcuL19nbG9iYWwnKVtLXTtcbn0pO1xuIiwidmFyIHBJRSA9IHJlcXVpcmUoJy4vX29iamVjdC1waWUnKTtcbnZhciBjcmVhdGVEZXNjID0gcmVxdWlyZSgnLi9fcHJvcGVydHktZGVzYycpO1xudmFyIHRvSU9iamVjdCA9IHJlcXVpcmUoJy4vX3RvLWlvYmplY3QnKTtcbnZhciB0b1ByaW1pdGl2ZSA9IHJlcXVpcmUoJy4vX3RvLXByaW1pdGl2ZScpO1xudmFyIGhhcyA9IHJlcXVpcmUoJy4vX2hhcycpO1xudmFyIElFOF9ET01fREVGSU5FID0gcmVxdWlyZSgnLi9faWU4LWRvbS1kZWZpbmUnKTtcbnZhciBnT1BEID0gT2JqZWN0LmdldE93blByb3BlcnR5RGVzY3JpcHRvcjtcblxuZXhwb3J0cy5mID0gcmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKSA/IGdPUEQgOiBmdW5jdGlvbiBnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IoTywgUCkge1xuICBPID0gdG9JT2JqZWN0KE8pO1xuICBQID0gdG9QcmltaXRpdmUoUCwgdHJ1ZSk7XG4gIGlmIChJRThfRE9NX0RFRklORSkgdHJ5IHtcbiAgICByZXR1cm4gZ09QRChPLCBQKTtcbiAgfSBjYXRjaCAoZSkgeyAvKiBlbXB0eSAqLyB9XG4gIGlmIChoYXMoTywgUCkpIHJldHVybiBjcmVhdGVEZXNjKCFwSUUuZi5jYWxsKE8sIFApLCBPW1BdKTtcbn07XG4iLCIvLyBmYWxsYmFjayBmb3IgSUUxMSBidWdneSBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyB3aXRoIGlmcmFtZSBhbmQgd2luZG93XG52YXIgdG9JT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8taW9iamVjdCcpO1xudmFyIGdPUE4gPSByZXF1aXJlKCcuL19vYmplY3QtZ29wbicpLmY7XG52YXIgdG9TdHJpbmcgPSB7fS50b1N0cmluZztcblxudmFyIHdpbmRvd05hbWVzID0gdHlwZW9mIHdpbmRvdyA9PSAnb2JqZWN0JyAmJiB3aW5kb3cgJiYgT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXNcbiAgPyBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyh3aW5kb3cpIDogW107XG5cbnZhciBnZXRXaW5kb3dOYW1lcyA9IGZ1bmN0aW9uIChpdCkge1xuICB0cnkge1xuICAgIHJldHVybiBnT1BOKGl0KTtcbiAgfSBjYXRjaCAoZSkge1xuICAgIHJldHVybiB3aW5kb3dOYW1lcy5zbGljZSgpO1xuICB9XG59O1xuXG5tb2R1bGUuZXhwb3J0cy5mID0gZnVuY3Rpb24gZ2V0T3duUHJvcGVydHlOYW1lcyhpdCkge1xuICByZXR1cm4gd2luZG93TmFtZXMgJiYgdG9TdHJpbmcuY2FsbChpdCkgPT0gJ1tvYmplY3QgV2luZG93XScgPyBnZXRXaW5kb3dOYW1lcyhpdCkgOiBnT1BOKHRvSU9iamVjdChpdCkpO1xufTtcbiIsIi8vIDE5LjEuMi43IC8gMTUuMi4zLjQgT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMoTylcbnZhciAka2V5cyA9IHJlcXVpcmUoJy4vX29iamVjdC1rZXlzLWludGVybmFsJyk7XG52YXIgaGlkZGVuS2V5cyA9IHJlcXVpcmUoJy4vX2VudW0tYnVnLWtleXMnKS5jb25jYXQoJ2xlbmd0aCcsICdwcm90b3R5cGUnKTtcblxuZXhwb3J0cy5mID0gT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMgfHwgZnVuY3Rpb24gZ2V0T3duUHJvcGVydHlOYW1lcyhPKSB7XG4gIHJldHVybiAka2V5cyhPLCBoaWRkZW5LZXlzKTtcbn07XG4iLCJleHBvcnRzLmYgPSBPYmplY3QuZ2V0T3duUHJvcGVydHlTeW1ib2xzO1xuIiwiLy8gMTkuMS4yLjkgLyAxNS4yLjMuMiBPYmplY3QuZ2V0UHJvdG90eXBlT2YoTylcbnZhciBoYXMgPSByZXF1aXJlKCcuL19oYXMnKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIElFX1BST1RPID0gcmVxdWlyZSgnLi9fc2hhcmVkLWtleScpKCdJRV9QUk9UTycpO1xudmFyIE9iamVjdFByb3RvID0gT2JqZWN0LnByb3RvdHlwZTtcblxubW9kdWxlLmV4cG9ydHMgPSBPYmplY3QuZ2V0UHJvdG90eXBlT2YgfHwgZnVuY3Rpb24gKE8pIHtcbiAgTyA9IHRvT2JqZWN0KE8pO1xuICBpZiAoaGFzKE8sIElFX1BST1RPKSkgcmV0dXJuIE9bSUVfUFJPVE9dO1xuICBpZiAodHlwZW9mIE8uY29uc3RydWN0b3IgPT0gJ2Z1bmN0aW9uJyAmJiBPIGluc3RhbmNlb2YgTy5jb25zdHJ1Y3Rvcikge1xuICAgIHJldHVybiBPLmNvbnN0cnVjdG9yLnByb3RvdHlwZTtcbiAgfSByZXR1cm4gTyBpbnN0YW5jZW9mIE9iamVjdCA/IE9iamVjdFByb3RvIDogbnVsbDtcbn07XG4iLCJ2YXIgaGFzID0gcmVxdWlyZSgnLi9faGFzJyk7XG52YXIgdG9JT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8taW9iamVjdCcpO1xudmFyIGFycmF5SW5kZXhPZiA9IHJlcXVpcmUoJy4vX2FycmF5LWluY2x1ZGVzJykoZmFsc2UpO1xudmFyIElFX1BST1RPID0gcmVxdWlyZSgnLi9fc2hhcmVkLWtleScpKCdJRV9QUk9UTycpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChvYmplY3QsIG5hbWVzKSB7XG4gIHZhciBPID0gdG9JT2JqZWN0KG9iamVjdCk7XG4gIHZhciBpID0gMDtcbiAgdmFyIHJlc3VsdCA9IFtdO1xuICB2YXIga2V5O1xuICBmb3IgKGtleSBpbiBPKSBpZiAoa2V5ICE9IElFX1BST1RPKSBoYXMoTywga2V5KSAmJiByZXN1bHQucHVzaChrZXkpO1xuICAvLyBEb24ndCBlbnVtIGJ1ZyAmIGhpZGRlbiBrZXlzXG4gIHdoaWxlIChuYW1lcy5sZW5ndGggPiBpKSBpZiAoaGFzKE8sIGtleSA9IG5hbWVzW2krK10pKSB7XG4gICAgfmFycmF5SW5kZXhPZihyZXN1bHQsIGtleSkgfHwgcmVzdWx0LnB1c2goa2V5KTtcbiAgfVxuICByZXR1cm4gcmVzdWx0O1xufTtcbiIsIi8vIDE5LjEuMi4xNCAvIDE1LjIuMy4xNCBPYmplY3Qua2V5cyhPKVxudmFyICRrZXlzID0gcmVxdWlyZSgnLi9fb2JqZWN0LWtleXMtaW50ZXJuYWwnKTtcbnZhciBlbnVtQnVnS2V5cyA9IHJlcXVpcmUoJy4vX2VudW0tYnVnLWtleXMnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBPYmplY3Qua2V5cyB8fCBmdW5jdGlvbiBrZXlzKE8pIHtcbiAgcmV0dXJuICRrZXlzKE8sIGVudW1CdWdLZXlzKTtcbn07XG4iLCJleHBvcnRzLmYgPSB7fS5wcm9wZXJ0eUlzRW51bWVyYWJsZTtcbiIsIi8vIG1vc3QgT2JqZWN0IG1ldGhvZHMgYnkgRVM2IHNob3VsZCBhY2NlcHQgcHJpbWl0aXZlc1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBjb3JlID0gcmVxdWlyZSgnLi9fY29yZScpO1xudmFyIGZhaWxzID0gcmVxdWlyZSgnLi9fZmFpbHMnKTtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKEtFWSwgZXhlYykge1xuICB2YXIgZm4gPSAoY29yZS5PYmplY3QgfHwge30pW0tFWV0gfHwgT2JqZWN0W0tFWV07XG4gIHZhciBleHAgPSB7fTtcbiAgZXhwW0tFWV0gPSBleGVjKGZuKTtcbiAgJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiBmYWlscyhmdW5jdGlvbiAoKSB7IGZuKDEpOyB9KSwgJ09iamVjdCcsIGV4cCk7XG59O1xuIiwidmFyIGdldEtleXMgPSByZXF1aXJlKCcuL19vYmplY3Qta2V5cycpO1xudmFyIHRvSU9iamVjdCA9IHJlcXVpcmUoJy4vX3RvLWlvYmplY3QnKTtcbnZhciBpc0VudW0gPSByZXF1aXJlKCcuL19vYmplY3QtcGllJykuZjtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGlzRW50cmllcykge1xuICByZXR1cm4gZnVuY3Rpb24gKGl0KSB7XG4gICAgdmFyIE8gPSB0b0lPYmplY3QoaXQpO1xuICAgIHZhciBrZXlzID0gZ2V0S2V5cyhPKTtcbiAgICB2YXIgbGVuZ3RoID0ga2V5cy5sZW5ndGg7XG4gICAgdmFyIGkgPSAwO1xuICAgIHZhciByZXN1bHQgPSBbXTtcbiAgICB2YXIga2V5O1xuICAgIHdoaWxlIChsZW5ndGggPiBpKSBpZiAoaXNFbnVtLmNhbGwoTywga2V5ID0ga2V5c1tpKytdKSkge1xuICAgICAgcmVzdWx0LnB1c2goaXNFbnRyaWVzID8gW2tleSwgT1trZXldXSA6IE9ba2V5XSk7XG4gICAgfSByZXR1cm4gcmVzdWx0O1xuICB9O1xufTtcbiIsIi8vIGFsbCBvYmplY3Qga2V5cywgaW5jbHVkZXMgbm9uLWVudW1lcmFibGUgYW5kIHN5bWJvbHNcbnZhciBnT1BOID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcG4nKTtcbnZhciBnT1BTID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcHMnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIFJlZmxlY3QgPSByZXF1aXJlKCcuL19nbG9iYWwnKS5SZWZsZWN0O1xubW9kdWxlLmV4cG9ydHMgPSBSZWZsZWN0ICYmIFJlZmxlY3Qub3duS2V5cyB8fCBmdW5jdGlvbiBvd25LZXlzKGl0KSB7XG4gIHZhciBrZXlzID0gZ09QTi5mKGFuT2JqZWN0KGl0KSk7XG4gIHZhciBnZXRTeW1ib2xzID0gZ09QUy5mO1xuICByZXR1cm4gZ2V0U3ltYm9scyA/IGtleXMuY29uY2F0KGdldFN5bWJvbHMoaXQpKSA6IGtleXM7XG59O1xuIiwidmFyICRwYXJzZUZsb2F0ID0gcmVxdWlyZSgnLi9fZ2xvYmFsJykucGFyc2VGbG9hdDtcbnZhciAkdHJpbSA9IHJlcXVpcmUoJy4vX3N0cmluZy10cmltJykudHJpbTtcblxubW9kdWxlLmV4cG9ydHMgPSAxIC8gJHBhcnNlRmxvYXQocmVxdWlyZSgnLi9fc3RyaW5nLXdzJykgKyAnLTAnKSAhPT0gLUluZmluaXR5ID8gZnVuY3Rpb24gcGFyc2VGbG9hdChzdHIpIHtcbiAgdmFyIHN0cmluZyA9ICR0cmltKFN0cmluZyhzdHIpLCAzKTtcbiAgdmFyIHJlc3VsdCA9ICRwYXJzZUZsb2F0KHN0cmluZyk7XG4gIHJldHVybiByZXN1bHQgPT09IDAgJiYgc3RyaW5nLmNoYXJBdCgwKSA9PSAnLScgPyAtMCA6IHJlc3VsdDtcbn0gOiAkcGFyc2VGbG9hdDtcbiIsInZhciAkcGFyc2VJbnQgPSByZXF1aXJlKCcuL19nbG9iYWwnKS5wYXJzZUludDtcbnZhciAkdHJpbSA9IHJlcXVpcmUoJy4vX3N0cmluZy10cmltJykudHJpbTtcbnZhciB3cyA9IHJlcXVpcmUoJy4vX3N0cmluZy13cycpO1xudmFyIGhleCA9IC9eWy0rXT8wW3hYXS87XG5cbm1vZHVsZS5leHBvcnRzID0gJHBhcnNlSW50KHdzICsgJzA4JykgIT09IDggfHwgJHBhcnNlSW50KHdzICsgJzB4MTYnKSAhPT0gMjIgPyBmdW5jdGlvbiBwYXJzZUludChzdHIsIHJhZGl4KSB7XG4gIHZhciBzdHJpbmcgPSAkdHJpbShTdHJpbmcoc3RyKSwgMyk7XG4gIHJldHVybiAkcGFyc2VJbnQoc3RyaW5nLCAocmFkaXggPj4+IDApIHx8IChoZXgudGVzdChzdHJpbmcpID8gMTYgOiAxMCkpO1xufSA6ICRwYXJzZUludDtcbiIsIm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGV4ZWMpIHtcbiAgdHJ5IHtcbiAgICByZXR1cm4geyBlOiBmYWxzZSwgdjogZXhlYygpIH07XG4gIH0gY2F0Y2ggKGUpIHtcbiAgICByZXR1cm4geyBlOiB0cnVlLCB2OiBlIH07XG4gIH1cbn07XG4iLCJ2YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xudmFyIG5ld1Byb21pc2VDYXBhYmlsaXR5ID0gcmVxdWlyZSgnLi9fbmV3LXByb21pc2UtY2FwYWJpbGl0eScpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChDLCB4KSB7XG4gIGFuT2JqZWN0KEMpO1xuICBpZiAoaXNPYmplY3QoeCkgJiYgeC5jb25zdHJ1Y3RvciA9PT0gQykgcmV0dXJuIHg7XG4gIHZhciBwcm9taXNlQ2FwYWJpbGl0eSA9IG5ld1Byb21pc2VDYXBhYmlsaXR5LmYoQyk7XG4gIHZhciByZXNvbHZlID0gcHJvbWlzZUNhcGFiaWxpdHkucmVzb2x2ZTtcbiAgcmVzb2x2ZSh4KTtcbiAgcmV0dXJuIHByb21pc2VDYXBhYmlsaXR5LnByb21pc2U7XG59O1xuIiwibW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoYml0bWFwLCB2YWx1ZSkge1xuICByZXR1cm4ge1xuICAgIGVudW1lcmFibGU6ICEoYml0bWFwICYgMSksXG4gICAgY29uZmlndXJhYmxlOiAhKGJpdG1hcCAmIDIpLFxuICAgIHdyaXRhYmxlOiAhKGJpdG1hcCAmIDQpLFxuICAgIHZhbHVlOiB2YWx1ZVxuICB9O1xufTtcbiIsInZhciByZWRlZmluZSA9IHJlcXVpcmUoJy4vX3JlZGVmaW5lJyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uICh0YXJnZXQsIHNyYywgc2FmZSkge1xuICBmb3IgKHZhciBrZXkgaW4gc3JjKSByZWRlZmluZSh0YXJnZXQsIGtleSwgc3JjW2tleV0sIHNhZmUpO1xuICByZXR1cm4gdGFyZ2V0O1xufTtcbiIsInZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciBoaWRlID0gcmVxdWlyZSgnLi9faGlkZScpO1xudmFyIGhhcyA9IHJlcXVpcmUoJy4vX2hhcycpO1xudmFyIFNSQyA9IHJlcXVpcmUoJy4vX3VpZCcpKCdzcmMnKTtcbnZhciAkdG9TdHJpbmcgPSByZXF1aXJlKCcuL19mdW5jdGlvbi10by1zdHJpbmcnKTtcbnZhciBUT19TVFJJTkcgPSAndG9TdHJpbmcnO1xudmFyIFRQTCA9ICgnJyArICR0b1N0cmluZykuc3BsaXQoVE9fU1RSSU5HKTtcblxucmVxdWlyZSgnLi9fY29yZScpLmluc3BlY3RTb3VyY2UgPSBmdW5jdGlvbiAoaXQpIHtcbiAgcmV0dXJuICR0b1N0cmluZy5jYWxsKGl0KTtcbn07XG5cbihtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChPLCBrZXksIHZhbCwgc2FmZSkge1xuICB2YXIgaXNGdW5jdGlvbiA9IHR5cGVvZiB2YWwgPT0gJ2Z1bmN0aW9uJztcbiAgaWYgKGlzRnVuY3Rpb24pIGhhcyh2YWwsICduYW1lJykgfHwgaGlkZSh2YWwsICduYW1lJywga2V5KTtcbiAgaWYgKE9ba2V5XSA9PT0gdmFsKSByZXR1cm47XG4gIGlmIChpc0Z1bmN0aW9uKSBoYXModmFsLCBTUkMpIHx8IGhpZGUodmFsLCBTUkMsIE9ba2V5XSA/ICcnICsgT1trZXldIDogVFBMLmpvaW4oU3RyaW5nKGtleSkpKTtcbiAgaWYgKE8gPT09IGdsb2JhbCkge1xuICAgIE9ba2V5XSA9IHZhbDtcbiAgfSBlbHNlIGlmICghc2FmZSkge1xuICAgIGRlbGV0ZSBPW2tleV07XG4gICAgaGlkZShPLCBrZXksIHZhbCk7XG4gIH0gZWxzZSBpZiAoT1trZXldKSB7XG4gICAgT1trZXldID0gdmFsO1xuICB9IGVsc2Uge1xuICAgIGhpZGUoTywga2V5LCB2YWwpO1xuICB9XG4vLyBhZGQgZmFrZSBGdW5jdGlvbiN0b1N0cmluZyBmb3IgY29ycmVjdCB3b3JrIHdyYXBwZWQgbWV0aG9kcyAvIGNvbnN0cnVjdG9ycyB3aXRoIG1ldGhvZHMgbGlrZSBMb0Rhc2ggaXNOYXRpdmVcbn0pKEZ1bmN0aW9uLnByb3RvdHlwZSwgVE9fU1RSSU5HLCBmdW5jdGlvbiB0b1N0cmluZygpIHtcbiAgcmV0dXJuIHR5cGVvZiB0aGlzID09ICdmdW5jdGlvbicgJiYgdGhpc1tTUkNdIHx8ICR0b1N0cmluZy5jYWxsKHRoaXMpO1xufSk7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBjbGFzc29mID0gcmVxdWlyZSgnLi9fY2xhc3NvZicpO1xudmFyIGJ1aWx0aW5FeGVjID0gUmVnRXhwLnByb3RvdHlwZS5leGVjO1xuXG4gLy8gYFJlZ0V4cEV4ZWNgIGFic3RyYWN0IG9wZXJhdGlvblxuLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9lY21hMjYyLyNzZWMtcmVnZXhwZXhlY1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoUiwgUykge1xuICB2YXIgZXhlYyA9IFIuZXhlYztcbiAgaWYgKHR5cGVvZiBleGVjID09PSAnZnVuY3Rpb24nKSB7XG4gICAgdmFyIHJlc3VsdCA9IGV4ZWMuY2FsbChSLCBTKTtcbiAgICBpZiAodHlwZW9mIHJlc3VsdCAhPT0gJ29iamVjdCcpIHtcbiAgICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1JlZ0V4cCBleGVjIG1ldGhvZCByZXR1cm5lZCBzb21ldGhpbmcgb3RoZXIgdGhhbiBhbiBPYmplY3Qgb3IgbnVsbCcpO1xuICAgIH1cbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG4gIGlmIChjbGFzc29mKFIpICE9PSAnUmVnRXhwJykge1xuICAgIHRocm93IG5ldyBUeXBlRXJyb3IoJ1JlZ0V4cCNleGVjIGNhbGxlZCBvbiBpbmNvbXBhdGlibGUgcmVjZWl2ZXInKTtcbiAgfVxuICByZXR1cm4gYnVpbHRpbkV4ZWMuY2FsbChSLCBTKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciByZWdleHBGbGFncyA9IHJlcXVpcmUoJy4vX2ZsYWdzJyk7XG5cbnZhciBuYXRpdmVFeGVjID0gUmVnRXhwLnByb3RvdHlwZS5leGVjO1xuLy8gVGhpcyBhbHdheXMgcmVmZXJzIHRvIHRoZSBuYXRpdmUgaW1wbGVtZW50YXRpb24sIGJlY2F1c2UgdGhlXG4vLyBTdHJpbmcjcmVwbGFjZSBwb2x5ZmlsbCB1c2VzIC4vZml4LXJlZ2V4cC13ZWxsLWtub3duLXN5bWJvbC1sb2dpYy5qcyxcbi8vIHdoaWNoIGxvYWRzIHRoaXMgZmlsZSBiZWZvcmUgcGF0Y2hpbmcgdGhlIG1ldGhvZC5cbnZhciBuYXRpdmVSZXBsYWNlID0gU3RyaW5nLnByb3RvdHlwZS5yZXBsYWNlO1xuXG52YXIgcGF0Y2hlZEV4ZWMgPSBuYXRpdmVFeGVjO1xuXG52YXIgTEFTVF9JTkRFWCA9ICdsYXN0SW5kZXgnO1xuXG52YXIgVVBEQVRFU19MQVNUX0lOREVYX1dST05HID0gKGZ1bmN0aW9uICgpIHtcbiAgdmFyIHJlMSA9IC9hLyxcbiAgICAgIHJlMiA9IC9iKi9nO1xuICBuYXRpdmVFeGVjLmNhbGwocmUxLCAnYScpO1xuICBuYXRpdmVFeGVjLmNhbGwocmUyLCAnYScpO1xuICByZXR1cm4gcmUxW0xBU1RfSU5ERVhdICE9PSAwIHx8IHJlMltMQVNUX0lOREVYXSAhPT0gMDtcbn0pKCk7XG5cbi8vIG5vbnBhcnRpY2lwYXRpbmcgY2FwdHVyaW5nIGdyb3VwLCBjb3BpZWQgZnJvbSBlczUtc2hpbSdzIFN0cmluZyNzcGxpdCBwYXRjaC5cbnZhciBOUENHX0lOQ0xVREVEID0gLygpPz8vLmV4ZWMoJycpWzFdICE9PSB1bmRlZmluZWQ7XG5cbnZhciBQQVRDSCA9IFVQREFURVNfTEFTVF9JTkRFWF9XUk9ORyB8fCBOUENHX0lOQ0xVREVEO1xuXG5pZiAoUEFUQ0gpIHtcbiAgcGF0Y2hlZEV4ZWMgPSBmdW5jdGlvbiBleGVjKHN0cikge1xuICAgIHZhciByZSA9IHRoaXM7XG4gICAgdmFyIGxhc3RJbmRleCwgcmVDb3B5LCBtYXRjaCwgaTtcblxuICAgIGlmIChOUENHX0lOQ0xVREVEKSB7XG4gICAgICByZUNvcHkgPSBuZXcgUmVnRXhwKCdeJyArIHJlLnNvdXJjZSArICckKD8hXFxcXHMpJywgcmVnZXhwRmxhZ3MuY2FsbChyZSkpO1xuICAgIH1cbiAgICBpZiAoVVBEQVRFU19MQVNUX0lOREVYX1dST05HKSBsYXN0SW5kZXggPSByZVtMQVNUX0lOREVYXTtcblxuICAgIG1hdGNoID0gbmF0aXZlRXhlYy5jYWxsKHJlLCBzdHIpO1xuXG4gICAgaWYgKFVQREFURVNfTEFTVF9JTkRFWF9XUk9ORyAmJiBtYXRjaCkge1xuICAgICAgcmVbTEFTVF9JTkRFWF0gPSByZS5nbG9iYWwgPyBtYXRjaC5pbmRleCArIG1hdGNoWzBdLmxlbmd0aCA6IGxhc3RJbmRleDtcbiAgICB9XG4gICAgaWYgKE5QQ0dfSU5DTFVERUQgJiYgbWF0Y2ggJiYgbWF0Y2gubGVuZ3RoID4gMSkge1xuICAgICAgLy8gRml4IGJyb3dzZXJzIHdob3NlIGBleGVjYCBtZXRob2RzIGRvbid0IGNvbnNpc3RlbnRseSByZXR1cm4gYHVuZGVmaW5lZGBcbiAgICAgIC8vIGZvciBOUENHLCBsaWtlIElFOC4gTk9URTogVGhpcyBkb2Vzbicgd29yayBmb3IgLyguPyk/L1xuICAgICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLWxvb3AtZnVuY1xuICAgICAgbmF0aXZlUmVwbGFjZS5jYWxsKG1hdGNoWzBdLCByZUNvcHksIGZ1bmN0aW9uICgpIHtcbiAgICAgICAgZm9yIChpID0gMTsgaSA8IGFyZ3VtZW50cy5sZW5ndGggLSAyOyBpKyspIHtcbiAgICAgICAgICBpZiAoYXJndW1lbnRzW2ldID09PSB1bmRlZmluZWQpIG1hdGNoW2ldID0gdW5kZWZpbmVkO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9XG5cbiAgICByZXR1cm4gbWF0Y2g7XG4gIH07XG59XG5cbm1vZHVsZS5leHBvcnRzID0gcGF0Y2hlZEV4ZWM7XG4iLCJtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChyZWdFeHAsIHJlcGxhY2UpIHtcbiAgdmFyIHJlcGxhY2VyID0gcmVwbGFjZSA9PT0gT2JqZWN0KHJlcGxhY2UpID8gZnVuY3Rpb24gKHBhcnQpIHtcbiAgICByZXR1cm4gcmVwbGFjZVtwYXJ0XTtcbiAgfSA6IHJlcGxhY2U7XG4gIHJldHVybiBmdW5jdGlvbiAoaXQpIHtcbiAgICByZXR1cm4gU3RyaW5nKGl0KS5yZXBsYWNlKHJlZ0V4cCwgcmVwbGFjZXIpO1xuICB9O1xufTtcbiIsIi8vIDcuMi45IFNhbWVWYWx1ZSh4LCB5KVxubW9kdWxlLmV4cG9ydHMgPSBPYmplY3QuaXMgfHwgZnVuY3Rpb24gaXMoeCwgeSkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tc2VsZi1jb21wYXJlXG4gIHJldHVybiB4ID09PSB5ID8geCAhPT0gMCB8fCAxIC8geCA9PT0gMSAvIHkgOiB4ICE9IHggJiYgeSAhPSB5O1xufTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIGh0dHBzOi8vdGMzOS5naXRodWIuaW8vcHJvcG9zYWwtc2V0bWFwLW9mZnJvbS9cbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgYUZ1bmN0aW9uID0gcmVxdWlyZSgnLi9fYS1mdW5jdGlvbicpO1xudmFyIGN0eCA9IHJlcXVpcmUoJy4vX2N0eCcpO1xudmFyIGZvck9mID0gcmVxdWlyZSgnLi9fZm9yLW9mJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKENPTExFQ1RJT04pIHtcbiAgJGV4cG9ydCgkZXhwb3J0LlMsIENPTExFQ1RJT04sIHsgZnJvbTogZnVuY3Rpb24gZnJvbShzb3VyY2UgLyogLCBtYXBGbiwgdGhpc0FyZyAqLykge1xuICAgIHZhciBtYXBGbiA9IGFyZ3VtZW50c1sxXTtcbiAgICB2YXIgbWFwcGluZywgQSwgbiwgY2I7XG4gICAgYUZ1bmN0aW9uKHRoaXMpO1xuICAgIG1hcHBpbmcgPSBtYXBGbiAhPT0gdW5kZWZpbmVkO1xuICAgIGlmIChtYXBwaW5nKSBhRnVuY3Rpb24obWFwRm4pO1xuICAgIGlmIChzb3VyY2UgPT0gdW5kZWZpbmVkKSByZXR1cm4gbmV3IHRoaXMoKTtcbiAgICBBID0gW107XG4gICAgaWYgKG1hcHBpbmcpIHtcbiAgICAgIG4gPSAwO1xuICAgICAgY2IgPSBjdHgobWFwRm4sIGFyZ3VtZW50c1syXSwgMik7XG4gICAgICBmb3JPZihzb3VyY2UsIGZhbHNlLCBmdW5jdGlvbiAobmV4dEl0ZW0pIHtcbiAgICAgICAgQS5wdXNoKGNiKG5leHRJdGVtLCBuKyspKTtcbiAgICAgIH0pO1xuICAgIH0gZWxzZSB7XG4gICAgICBmb3JPZihzb3VyY2UsIGZhbHNlLCBBLnB1c2gsIEEpO1xuICAgIH1cbiAgICByZXR1cm4gbmV3IHRoaXMoQSk7XG4gIH0gfSk7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9wcm9wb3NhbC1zZXRtYXAtb2Zmcm9tL1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoQ09MTEVDVElPTikge1xuICAkZXhwb3J0KCRleHBvcnQuUywgQ09MTEVDVElPTiwgeyBvZjogZnVuY3Rpb24gb2YoKSB7XG4gICAgdmFyIGxlbmd0aCA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gICAgdmFyIEEgPSBuZXcgQXJyYXkobGVuZ3RoKTtcbiAgICB3aGlsZSAobGVuZ3RoLS0pIEFbbGVuZ3RoXSA9IGFyZ3VtZW50c1tsZW5ndGhdO1xuICAgIHJldHVybiBuZXcgdGhpcyhBKTtcbiAgfSB9KTtcbn07XG4iLCIvLyBXb3JrcyB3aXRoIF9fcHJvdG9fXyBvbmx5LiBPbGQgdjggY2FuJ3Qgd29yayB3aXRoIG51bGwgcHJvdG8gb2JqZWN0cy5cbi8qIGVzbGludC1kaXNhYmxlIG5vLXByb3RvICovXG52YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIGNoZWNrID0gZnVuY3Rpb24gKE8sIHByb3RvKSB7XG4gIGFuT2JqZWN0KE8pO1xuICBpZiAoIWlzT2JqZWN0KHByb3RvKSAmJiBwcm90byAhPT0gbnVsbCkgdGhyb3cgVHlwZUVycm9yKHByb3RvICsgXCI6IGNhbid0IHNldCBhcyBwcm90b3R5cGUhXCIpO1xufTtcbm1vZHVsZS5leHBvcnRzID0ge1xuICBzZXQ6IE9iamVjdC5zZXRQcm90b3R5cGVPZiB8fCAoJ19fcHJvdG9fXycgaW4ge30gPyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lXG4gICAgZnVuY3Rpb24gKHRlc3QsIGJ1Z2d5LCBzZXQpIHtcbiAgICAgIHRyeSB7XG4gICAgICAgIHNldCA9IHJlcXVpcmUoJy4vX2N0eCcpKEZ1bmN0aW9uLmNhbGwsIHJlcXVpcmUoJy4vX29iamVjdC1nb3BkJykuZihPYmplY3QucHJvdG90eXBlLCAnX19wcm90b19fJykuc2V0LCAyKTtcbiAgICAgICAgc2V0KHRlc3QsIFtdKTtcbiAgICAgICAgYnVnZ3kgPSAhKHRlc3QgaW5zdGFuY2VvZiBBcnJheSk7XG4gICAgICB9IGNhdGNoIChlKSB7IGJ1Z2d5ID0gdHJ1ZTsgfVxuICAgICAgcmV0dXJuIGZ1bmN0aW9uIHNldFByb3RvdHlwZU9mKE8sIHByb3RvKSB7XG4gICAgICAgIGNoZWNrKE8sIHByb3RvKTtcbiAgICAgICAgaWYgKGJ1Z2d5KSBPLl9fcHJvdG9fXyA9IHByb3RvO1xuICAgICAgICBlbHNlIHNldChPLCBwcm90byk7XG4gICAgICAgIHJldHVybiBPO1xuICAgICAgfTtcbiAgICB9KHt9LCBmYWxzZSkgOiB1bmRlZmluZWQpLFxuICBjaGVjazogY2hlY2tcbn07XG4iLCIndXNlIHN0cmljdCc7XG52YXIgZ2xvYmFsID0gcmVxdWlyZSgnLi9fZ2xvYmFsJyk7XG52YXIgZFAgPSByZXF1aXJlKCcuL19vYmplY3QtZHAnKTtcbnZhciBERVNDUklQVE9SUyA9IHJlcXVpcmUoJy4vX2Rlc2NyaXB0b3JzJyk7XG52YXIgU1BFQ0lFUyA9IHJlcXVpcmUoJy4vX3drcycpKCdzcGVjaWVzJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKEtFWSkge1xuICB2YXIgQyA9IGdsb2JhbFtLRVldO1xuICBpZiAoREVTQ1JJUFRPUlMgJiYgQyAmJiAhQ1tTUEVDSUVTXSkgZFAuZihDLCBTUEVDSUVTLCB7XG4gICAgY29uZmlndXJhYmxlOiB0cnVlLFxuICAgIGdldDogZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpczsgfVxuICB9KTtcbn07XG4iLCJ2YXIgZGVmID0gcmVxdWlyZSgnLi9fb2JqZWN0LWRwJykuZjtcbnZhciBoYXMgPSByZXF1aXJlKCcuL19oYXMnKTtcbnZhciBUQUcgPSByZXF1aXJlKCcuL193a3MnKSgndG9TdHJpbmdUYWcnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaXQsIHRhZywgc3RhdCkge1xuICBpZiAoaXQgJiYgIWhhcyhpdCA9IHN0YXQgPyBpdCA6IGl0LnByb3RvdHlwZSwgVEFHKSkgZGVmKGl0LCBUQUcsIHsgY29uZmlndXJhYmxlOiB0cnVlLCB2YWx1ZTogdGFnIH0pO1xufTtcbiIsInZhciBzaGFyZWQgPSByZXF1aXJlKCcuL19zaGFyZWQnKSgna2V5cycpO1xudmFyIHVpZCA9IHJlcXVpcmUoJy4vX3VpZCcpO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoa2V5KSB7XG4gIHJldHVybiBzaGFyZWRba2V5XSB8fCAoc2hhcmVkW2tleV0gPSB1aWQoa2V5KSk7XG59O1xuIiwidmFyIGNvcmUgPSByZXF1aXJlKCcuL19jb3JlJyk7XG52YXIgZ2xvYmFsID0gcmVxdWlyZSgnLi9fZ2xvYmFsJyk7XG52YXIgU0hBUkVEID0gJ19fY29yZS1qc19zaGFyZWRfXyc7XG52YXIgc3RvcmUgPSBnbG9iYWxbU0hBUkVEXSB8fCAoZ2xvYmFsW1NIQVJFRF0gPSB7fSk7XG5cbihtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChrZXksIHZhbHVlKSB7XG4gIHJldHVybiBzdG9yZVtrZXldIHx8IChzdG9yZVtrZXldID0gdmFsdWUgIT09IHVuZGVmaW5lZCA/IHZhbHVlIDoge30pO1xufSkoJ3ZlcnNpb25zJywgW10pLnB1c2goe1xuICB2ZXJzaW9uOiBjb3JlLnZlcnNpb24sXG4gIG1vZGU6IHJlcXVpcmUoJy4vX2xpYnJhcnknKSA/ICdwdXJlJyA6ICdnbG9iYWwnLFxuICBjb3B5cmlnaHQ6ICfCqSAyMDE5IERlbmlzIFB1c2hrYXJldiAoemxvaXJvY2sucnUpJ1xufSk7XG4iLCIvLyA3LjMuMjAgU3BlY2llc0NvbnN0cnVjdG9yKE8sIGRlZmF1bHRDb25zdHJ1Y3RvcilcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIGFGdW5jdGlvbiA9IHJlcXVpcmUoJy4vX2EtZnVuY3Rpb24nKTtcbnZhciBTUEVDSUVTID0gcmVxdWlyZSgnLi9fd2tzJykoJ3NwZWNpZXMnKTtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKE8sIEQpIHtcbiAgdmFyIEMgPSBhbk9iamVjdChPKS5jb25zdHJ1Y3RvcjtcbiAgdmFyIFM7XG4gIHJldHVybiBDID09PSB1bmRlZmluZWQgfHwgKFMgPSBhbk9iamVjdChDKVtTUEVDSUVTXSkgPT0gdW5kZWZpbmVkID8gRCA6IGFGdW5jdGlvbihTKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG52YXIgZmFpbHMgPSByZXF1aXJlKCcuL19mYWlscycpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChtZXRob2QsIGFyZykge1xuICByZXR1cm4gISFtZXRob2QgJiYgZmFpbHMoZnVuY3Rpb24gKCkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11c2VsZXNzLWNhbGxcbiAgICBhcmcgPyBtZXRob2QuY2FsbChudWxsLCBmdW5jdGlvbiAoKSB7IC8qIGVtcHR5ICovIH0sIDEpIDogbWV0aG9kLmNhbGwobnVsbCk7XG4gIH0pO1xufTtcbiIsInZhciB0b0ludGVnZXIgPSByZXF1aXJlKCcuL190by1pbnRlZ2VyJyk7XG52YXIgZGVmaW5lZCA9IHJlcXVpcmUoJy4vX2RlZmluZWQnKTtcbi8vIHRydWUgIC0+IFN0cmluZyNhdFxuLy8gZmFsc2UgLT4gU3RyaW5nI2NvZGVQb2ludEF0XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChUT19TVFJJTkcpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uICh0aGF0LCBwb3MpIHtcbiAgICB2YXIgcyA9IFN0cmluZyhkZWZpbmVkKHRoYXQpKTtcbiAgICB2YXIgaSA9IHRvSW50ZWdlcihwb3MpO1xuICAgIHZhciBsID0gcy5sZW5ndGg7XG4gICAgdmFyIGEsIGI7XG4gICAgaWYgKGkgPCAwIHx8IGkgPj0gbCkgcmV0dXJuIFRPX1NUUklORyA/ICcnIDogdW5kZWZpbmVkO1xuICAgIGEgPSBzLmNoYXJDb2RlQXQoaSk7XG4gICAgcmV0dXJuIGEgPCAweGQ4MDAgfHwgYSA+IDB4ZGJmZiB8fCBpICsgMSA9PT0gbCB8fCAoYiA9IHMuY2hhckNvZGVBdChpICsgMSkpIDwgMHhkYzAwIHx8IGIgPiAweGRmZmZcbiAgICAgID8gVE9fU1RSSU5HID8gcy5jaGFyQXQoaSkgOiBhXG4gICAgICA6IFRPX1NUUklORyA/IHMuc2xpY2UoaSwgaSArIDIpIDogKGEgLSAweGQ4MDAgPDwgMTApICsgKGIgLSAweGRjMDApICsgMHgxMDAwMDtcbiAgfTtcbn07XG4iLCIvLyBoZWxwZXIgZm9yIFN0cmluZyN7c3RhcnRzV2l0aCwgZW5kc1dpdGgsIGluY2x1ZGVzfVxudmFyIGlzUmVnRXhwID0gcmVxdWlyZSgnLi9faXMtcmVnZXhwJyk7XG52YXIgZGVmaW5lZCA9IHJlcXVpcmUoJy4vX2RlZmluZWQnKTtcblxubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAodGhhdCwgc2VhcmNoU3RyaW5nLCBOQU1FKSB7XG4gIGlmIChpc1JlZ0V4cChzZWFyY2hTdHJpbmcpKSB0aHJvdyBUeXBlRXJyb3IoJ1N0cmluZyMnICsgTkFNRSArIFwiIGRvZXNuJ3QgYWNjZXB0IHJlZ2V4IVwiKTtcbiAgcmV0dXJuIFN0cmluZyhkZWZpbmVkKHRoYXQpKTtcbn07XG4iLCJ2YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGZhaWxzID0gcmVxdWlyZSgnLi9fZmFpbHMnKTtcbnZhciBkZWZpbmVkID0gcmVxdWlyZSgnLi9fZGVmaW5lZCcpO1xudmFyIHF1b3QgPSAvXCIvZztcbi8vIEIuMi4zLjIuMSBDcmVhdGVIVE1MKHN0cmluZywgdGFnLCBhdHRyaWJ1dGUsIHZhbHVlKVxudmFyIGNyZWF0ZUhUTUwgPSBmdW5jdGlvbiAoc3RyaW5nLCB0YWcsIGF0dHJpYnV0ZSwgdmFsdWUpIHtcbiAgdmFyIFMgPSBTdHJpbmcoZGVmaW5lZChzdHJpbmcpKTtcbiAgdmFyIHAxID0gJzwnICsgdGFnO1xuICBpZiAoYXR0cmlidXRlICE9PSAnJykgcDEgKz0gJyAnICsgYXR0cmlidXRlICsgJz1cIicgKyBTdHJpbmcodmFsdWUpLnJlcGxhY2UocXVvdCwgJyZxdW90OycpICsgJ1wiJztcbiAgcmV0dXJuIHAxICsgJz4nICsgUyArICc8LycgKyB0YWcgKyAnPic7XG59O1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoTkFNRSwgZXhlYykge1xuICB2YXIgTyA9IHt9O1xuICBPW05BTUVdID0gZXhlYyhjcmVhdGVIVE1MKTtcbiAgJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiBmYWlscyhmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHRlc3QgPSAnJ1tOQU1FXSgnXCInKTtcbiAgICByZXR1cm4gdGVzdCAhPT0gdGVzdC50b0xvd2VyQ2FzZSgpIHx8IHRlc3Quc3BsaXQoJ1wiJykubGVuZ3RoID4gMztcbiAgfSksICdTdHJpbmcnLCBPKTtcbn07XG4iLCIvLyBodHRwczovL2dpdGh1Yi5jb20vdGMzOS9wcm9wb3NhbC1zdHJpbmctcGFkLXN0YXJ0LWVuZFxudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG52YXIgcmVwZWF0ID0gcmVxdWlyZSgnLi9fc3RyaW5nLXJlcGVhdCcpO1xudmFyIGRlZmluZWQgPSByZXF1aXJlKCcuL19kZWZpbmVkJyk7XG5cbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKHRoYXQsIG1heExlbmd0aCwgZmlsbFN0cmluZywgbGVmdCkge1xuICB2YXIgUyA9IFN0cmluZyhkZWZpbmVkKHRoYXQpKTtcbiAgdmFyIHN0cmluZ0xlbmd0aCA9IFMubGVuZ3RoO1xuICB2YXIgZmlsbFN0ciA9IGZpbGxTdHJpbmcgPT09IHVuZGVmaW5lZCA/ICcgJyA6IFN0cmluZyhmaWxsU3RyaW5nKTtcbiAgdmFyIGludE1heExlbmd0aCA9IHRvTGVuZ3RoKG1heExlbmd0aCk7XG4gIGlmIChpbnRNYXhMZW5ndGggPD0gc3RyaW5nTGVuZ3RoIHx8IGZpbGxTdHIgPT0gJycpIHJldHVybiBTO1xuICB2YXIgZmlsbExlbiA9IGludE1heExlbmd0aCAtIHN0cmluZ0xlbmd0aDtcbiAgdmFyIHN0cmluZ0ZpbGxlciA9IHJlcGVhdC5jYWxsKGZpbGxTdHIsIE1hdGguY2VpbChmaWxsTGVuIC8gZmlsbFN0ci5sZW5ndGgpKTtcbiAgaWYgKHN0cmluZ0ZpbGxlci5sZW5ndGggPiBmaWxsTGVuKSBzdHJpbmdGaWxsZXIgPSBzdHJpbmdGaWxsZXIuc2xpY2UoMCwgZmlsbExlbik7XG4gIHJldHVybiBsZWZ0ID8gc3RyaW5nRmlsbGVyICsgUyA6IFMgKyBzdHJpbmdGaWxsZXI7XG59O1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIHRvSW50ZWdlciA9IHJlcXVpcmUoJy4vX3RvLWludGVnZXInKTtcbnZhciBkZWZpbmVkID0gcmVxdWlyZSgnLi9fZGVmaW5lZCcpO1xuXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIHJlcGVhdChjb3VudCkge1xuICB2YXIgc3RyID0gU3RyaW5nKGRlZmluZWQodGhpcykpO1xuICB2YXIgcmVzID0gJyc7XG4gIHZhciBuID0gdG9JbnRlZ2VyKGNvdW50KTtcbiAgaWYgKG4gPCAwIHx8IG4gPT0gSW5maW5pdHkpIHRocm93IFJhbmdlRXJyb3IoXCJDb3VudCBjYW4ndCBiZSBuZWdhdGl2ZVwiKTtcbiAgZm9yICg7biA+IDA7IChuID4+Pj0gMSkgJiYgKHN0ciArPSBzdHIpKSBpZiAobiAmIDEpIHJlcyArPSBzdHI7XG4gIHJldHVybiByZXM7XG59O1xuIiwidmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBkZWZpbmVkID0gcmVxdWlyZSgnLi9fZGVmaW5lZCcpO1xudmFyIGZhaWxzID0gcmVxdWlyZSgnLi9fZmFpbHMnKTtcbnZhciBzcGFjZXMgPSByZXF1aXJlKCcuL19zdHJpbmctd3MnKTtcbnZhciBzcGFjZSA9ICdbJyArIHNwYWNlcyArICddJztcbnZhciBub24gPSAnXFx1MjAwYlxcdTAwODUnO1xudmFyIGx0cmltID0gUmVnRXhwKCdeJyArIHNwYWNlICsgc3BhY2UgKyAnKicpO1xudmFyIHJ0cmltID0gUmVnRXhwKHNwYWNlICsgc3BhY2UgKyAnKiQnKTtcblxudmFyIGV4cG9ydGVyID0gZnVuY3Rpb24gKEtFWSwgZXhlYywgQUxJQVMpIHtcbiAgdmFyIGV4cCA9IHt9O1xuICB2YXIgRk9SQ0UgPSBmYWlscyhmdW5jdGlvbiAoKSB7XG4gICAgcmV0dXJuICEhc3BhY2VzW0tFWV0oKSB8fCBub25bS0VZXSgpICE9IG5vbjtcbiAgfSk7XG4gIHZhciBmbiA9IGV4cFtLRVldID0gRk9SQ0UgPyBleGVjKHRyaW0pIDogc3BhY2VzW0tFWV07XG4gIGlmIChBTElBUykgZXhwW0FMSUFTXSA9IGZuO1xuICAkZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqIEZPUkNFLCAnU3RyaW5nJywgZXhwKTtcbn07XG5cbi8vIDEgLT4gU3RyaW5nI3RyaW1MZWZ0XG4vLyAyIC0+IFN0cmluZyN0cmltUmlnaHRcbi8vIDMgLT4gU3RyaW5nI3RyaW1cbnZhciB0cmltID0gZXhwb3J0ZXIudHJpbSA9IGZ1bmN0aW9uIChzdHJpbmcsIFRZUEUpIHtcbiAgc3RyaW5nID0gU3RyaW5nKGRlZmluZWQoc3RyaW5nKSk7XG4gIGlmIChUWVBFICYgMSkgc3RyaW5nID0gc3RyaW5nLnJlcGxhY2UobHRyaW0sICcnKTtcbiAgaWYgKFRZUEUgJiAyKSBzdHJpbmcgPSBzdHJpbmcucmVwbGFjZShydHJpbSwgJycpO1xuICByZXR1cm4gc3RyaW5nO1xufTtcblxubW9kdWxlLmV4cG9ydHMgPSBleHBvcnRlcjtcbiIsIm1vZHVsZS5leHBvcnRzID0gJ1xceDA5XFx4MEFcXHgwQlxceDBDXFx4MERcXHgyMFxceEEwXFx1MTY4MFxcdTE4MEVcXHUyMDAwXFx1MjAwMVxcdTIwMDJcXHUyMDAzJyArXG4gICdcXHUyMDA0XFx1MjAwNVxcdTIwMDZcXHUyMDA3XFx1MjAwOFxcdTIwMDlcXHUyMDBBXFx1MjAyRlxcdTIwNUZcXHUzMDAwXFx1MjAyOFxcdTIwMjlcXHVGRUZGJztcbiIsInZhciBjdHggPSByZXF1aXJlKCcuL19jdHgnKTtcbnZhciBpbnZva2UgPSByZXF1aXJlKCcuL19pbnZva2UnKTtcbnZhciBodG1sID0gcmVxdWlyZSgnLi9faHRtbCcpO1xudmFyIGNlbCA9IHJlcXVpcmUoJy4vX2RvbS1jcmVhdGUnKTtcbnZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciBwcm9jZXNzID0gZ2xvYmFsLnByb2Nlc3M7XG52YXIgc2V0VGFzayA9IGdsb2JhbC5zZXRJbW1lZGlhdGU7XG52YXIgY2xlYXJUYXNrID0gZ2xvYmFsLmNsZWFySW1tZWRpYXRlO1xudmFyIE1lc3NhZ2VDaGFubmVsID0gZ2xvYmFsLk1lc3NhZ2VDaGFubmVsO1xudmFyIERpc3BhdGNoID0gZ2xvYmFsLkRpc3BhdGNoO1xudmFyIGNvdW50ZXIgPSAwO1xudmFyIHF1ZXVlID0ge307XG52YXIgT05SRUFEWVNUQVRFQ0hBTkdFID0gJ29ucmVhZHlzdGF0ZWNoYW5nZSc7XG52YXIgZGVmZXIsIGNoYW5uZWwsIHBvcnQ7XG52YXIgcnVuID0gZnVuY3Rpb24gKCkge1xuICB2YXIgaWQgPSArdGhpcztcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXByb3RvdHlwZS1idWlsdGluc1xuICBpZiAocXVldWUuaGFzT3duUHJvcGVydHkoaWQpKSB7XG4gICAgdmFyIGZuID0gcXVldWVbaWRdO1xuICAgIGRlbGV0ZSBxdWV1ZVtpZF07XG4gICAgZm4oKTtcbiAgfVxufTtcbnZhciBsaXN0ZW5lciA9IGZ1bmN0aW9uIChldmVudCkge1xuICBydW4uY2FsbChldmVudC5kYXRhKTtcbn07XG4vLyBOb2RlLmpzIDAuOSsgJiBJRTEwKyBoYXMgc2V0SW1tZWRpYXRlLCBvdGhlcndpc2U6XG5pZiAoIXNldFRhc2sgfHwgIWNsZWFyVGFzaykge1xuICBzZXRUYXNrID0gZnVuY3Rpb24gc2V0SW1tZWRpYXRlKGZuKSB7XG4gICAgdmFyIGFyZ3MgPSBbXTtcbiAgICB2YXIgaSA9IDE7XG4gICAgd2hpbGUgKGFyZ3VtZW50cy5sZW5ndGggPiBpKSBhcmdzLnB1c2goYXJndW1lbnRzW2krK10pO1xuICAgIHF1ZXVlWysrY291bnRlcl0gPSBmdW5jdGlvbiAoKSB7XG4gICAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tbmV3LWZ1bmNcbiAgICAgIGludm9rZSh0eXBlb2YgZm4gPT0gJ2Z1bmN0aW9uJyA/IGZuIDogRnVuY3Rpb24oZm4pLCBhcmdzKTtcbiAgICB9O1xuICAgIGRlZmVyKGNvdW50ZXIpO1xuICAgIHJldHVybiBjb3VudGVyO1xuICB9O1xuICBjbGVhclRhc2sgPSBmdW5jdGlvbiBjbGVhckltbWVkaWF0ZShpZCkge1xuICAgIGRlbGV0ZSBxdWV1ZVtpZF07XG4gIH07XG4gIC8vIE5vZGUuanMgMC44LVxuICBpZiAocmVxdWlyZSgnLi9fY29mJykocHJvY2VzcykgPT0gJ3Byb2Nlc3MnKSB7XG4gICAgZGVmZXIgPSBmdW5jdGlvbiAoaWQpIHtcbiAgICAgIHByb2Nlc3MubmV4dFRpY2soY3R4KHJ1biwgaWQsIDEpKTtcbiAgICB9O1xuICAvLyBTcGhlcmUgKEpTIGdhbWUgZW5naW5lKSBEaXNwYXRjaCBBUElcbiAgfSBlbHNlIGlmIChEaXNwYXRjaCAmJiBEaXNwYXRjaC5ub3cpIHtcbiAgICBkZWZlciA9IGZ1bmN0aW9uIChpZCkge1xuICAgICAgRGlzcGF0Y2gubm93KGN0eChydW4sIGlkLCAxKSk7XG4gICAgfTtcbiAgLy8gQnJvd3NlcnMgd2l0aCBNZXNzYWdlQ2hhbm5lbCwgaW5jbHVkZXMgV2ViV29ya2Vyc1xuICB9IGVsc2UgaWYgKE1lc3NhZ2VDaGFubmVsKSB7XG4gICAgY2hhbm5lbCA9IG5ldyBNZXNzYWdlQ2hhbm5lbCgpO1xuICAgIHBvcnQgPSBjaGFubmVsLnBvcnQyO1xuICAgIGNoYW5uZWwucG9ydDEub25tZXNzYWdlID0gbGlzdGVuZXI7XG4gICAgZGVmZXIgPSBjdHgocG9ydC5wb3N0TWVzc2FnZSwgcG9ydCwgMSk7XG4gIC8vIEJyb3dzZXJzIHdpdGggcG9zdE1lc3NhZ2UsIHNraXAgV2ViV29ya2Vyc1xuICAvLyBJRTggaGFzIHBvc3RNZXNzYWdlLCBidXQgaXQncyBzeW5jICYgdHlwZW9mIGl0cyBwb3N0TWVzc2FnZSBpcyAnb2JqZWN0J1xuICB9IGVsc2UgaWYgKGdsb2JhbC5hZGRFdmVudExpc3RlbmVyICYmIHR5cGVvZiBwb3N0TWVzc2FnZSA9PSAnZnVuY3Rpb24nICYmICFnbG9iYWwuaW1wb3J0U2NyaXB0cykge1xuICAgIGRlZmVyID0gZnVuY3Rpb24gKGlkKSB7XG4gICAgICBnbG9iYWwucG9zdE1lc3NhZ2UoaWQgKyAnJywgJyonKTtcbiAgICB9O1xuICAgIGdsb2JhbC5hZGRFdmVudExpc3RlbmVyKCdtZXNzYWdlJywgbGlzdGVuZXIsIGZhbHNlKTtcbiAgLy8gSUU4LVxuICB9IGVsc2UgaWYgKE9OUkVBRFlTVEFURUNIQU5HRSBpbiBjZWwoJ3NjcmlwdCcpKSB7XG4gICAgZGVmZXIgPSBmdW5jdGlvbiAoaWQpIHtcbiAgICAgIGh0bWwuYXBwZW5kQ2hpbGQoY2VsKCdzY3JpcHQnKSlbT05SRUFEWVNUQVRFQ0hBTkdFXSA9IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgaHRtbC5yZW1vdmVDaGlsZCh0aGlzKTtcbiAgICAgICAgcnVuLmNhbGwoaWQpO1xuICAgICAgfTtcbiAgICB9O1xuICAvLyBSZXN0IG9sZCBicm93c2Vyc1xuICB9IGVsc2Uge1xuICAgIGRlZmVyID0gZnVuY3Rpb24gKGlkKSB7XG4gICAgICBzZXRUaW1lb3V0KGN0eChydW4sIGlkLCAxKSwgMCk7XG4gICAgfTtcbiAgfVxufVxubW9kdWxlLmV4cG9ydHMgPSB7XG4gIHNldDogc2V0VGFzayxcbiAgY2xlYXI6IGNsZWFyVGFza1xufTtcbiIsInZhciB0b0ludGVnZXIgPSByZXF1aXJlKCcuL190by1pbnRlZ2VyJyk7XG52YXIgbWF4ID0gTWF0aC5tYXg7XG52YXIgbWluID0gTWF0aC5taW47XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpbmRleCwgbGVuZ3RoKSB7XG4gIGluZGV4ID0gdG9JbnRlZ2VyKGluZGV4KTtcbiAgcmV0dXJuIGluZGV4IDwgMCA/IG1heChpbmRleCArIGxlbmd0aCwgMCkgOiBtaW4oaW5kZXgsIGxlbmd0aCk7XG59O1xuIiwiLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9lY21hMjYyLyNzZWMtdG9pbmRleFxudmFyIHRvSW50ZWdlciA9IHJlcXVpcmUoJy4vX3RvLWludGVnZXInKTtcbnZhciB0b0xlbmd0aCA9IHJlcXVpcmUoJy4vX3RvLWxlbmd0aCcpO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaXQpIHtcbiAgaWYgKGl0ID09PSB1bmRlZmluZWQpIHJldHVybiAwO1xuICB2YXIgbnVtYmVyID0gdG9JbnRlZ2VyKGl0KTtcbiAgdmFyIGxlbmd0aCA9IHRvTGVuZ3RoKG51bWJlcik7XG4gIGlmIChudW1iZXIgIT09IGxlbmd0aCkgdGhyb3cgUmFuZ2VFcnJvcignV3JvbmcgbGVuZ3RoIScpO1xuICByZXR1cm4gbGVuZ3RoO1xufTtcbiIsIi8vIDcuMS40IFRvSW50ZWdlclxudmFyIGNlaWwgPSBNYXRoLmNlaWw7XG52YXIgZmxvb3IgPSBNYXRoLmZsb29yO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaXQpIHtcbiAgcmV0dXJuIGlzTmFOKGl0ID0gK2l0KSA/IDAgOiAoaXQgPiAwID8gZmxvb3IgOiBjZWlsKShpdCk7XG59O1xuIiwiLy8gdG8gaW5kZXhlZCBvYmplY3QsIHRvT2JqZWN0IHdpdGggZmFsbGJhY2sgZm9yIG5vbi1hcnJheS1saWtlIEVTMyBzdHJpbmdzXG52YXIgSU9iamVjdCA9IHJlcXVpcmUoJy4vX2lvYmplY3QnKTtcbnZhciBkZWZpbmVkID0gcmVxdWlyZSgnLi9fZGVmaW5lZCcpO1xubW9kdWxlLmV4cG9ydHMgPSBmdW5jdGlvbiAoaXQpIHtcbiAgcmV0dXJuIElPYmplY3QoZGVmaW5lZChpdCkpO1xufTtcbiIsIi8vIDcuMS4xNSBUb0xlbmd0aFxudmFyIHRvSW50ZWdlciA9IHJlcXVpcmUoJy4vX3RvLWludGVnZXInKTtcbnZhciBtaW4gPSBNYXRoLm1pbjtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGl0KSB7XG4gIHJldHVybiBpdCA+IDAgPyBtaW4odG9JbnRlZ2VyKGl0KSwgMHgxZmZmZmZmZmZmZmZmZikgOiAwOyAvLyBwb3coMiwgNTMpIC0gMSA9PSA5MDA3MTk5MjU0NzQwOTkxXG59O1xuIiwiLy8gNy4xLjEzIFRvT2JqZWN0KGFyZ3VtZW50KVxudmFyIGRlZmluZWQgPSByZXF1aXJlKCcuL19kZWZpbmVkJyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCkge1xuICByZXR1cm4gT2JqZWN0KGRlZmluZWQoaXQpKTtcbn07XG4iLCIvLyA3LjEuMSBUb1ByaW1pdGl2ZShpbnB1dCBbLCBQcmVmZXJyZWRUeXBlXSlcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xuLy8gaW5zdGVhZCBvZiB0aGUgRVM2IHNwZWMgdmVyc2lvbiwgd2UgZGlkbid0IGltcGxlbWVudCBAQHRvUHJpbWl0aXZlIGNhc2Vcbi8vIGFuZCB0aGUgc2Vjb25kIGFyZ3VtZW50IC0gZmxhZyAtIHByZWZlcnJlZCB0eXBlIGlzIGEgc3RyaW5nXG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCwgUykge1xuICBpZiAoIWlzT2JqZWN0KGl0KSkgcmV0dXJuIGl0O1xuICB2YXIgZm4sIHZhbDtcbiAgaWYgKFMgJiYgdHlwZW9mIChmbiA9IGl0LnRvU3RyaW5nKSA9PSAnZnVuY3Rpb24nICYmICFpc09iamVjdCh2YWwgPSBmbi5jYWxsKGl0KSkpIHJldHVybiB2YWw7XG4gIGlmICh0eXBlb2YgKGZuID0gaXQudmFsdWVPZikgPT0gJ2Z1bmN0aW9uJyAmJiAhaXNPYmplY3QodmFsID0gZm4uY2FsbChpdCkpKSByZXR1cm4gdmFsO1xuICBpZiAoIVMgJiYgdHlwZW9mIChmbiA9IGl0LnRvU3RyaW5nKSA9PSAnZnVuY3Rpb24nICYmICFpc09iamVjdCh2YWwgPSBmbi5jYWxsKGl0KSkpIHJldHVybiB2YWw7XG4gIHRocm93IFR5cGVFcnJvcihcIkNhbid0IGNvbnZlcnQgb2JqZWN0IHRvIHByaW1pdGl2ZSB2YWx1ZVwiKTtcbn07XG4iLCIndXNlIHN0cmljdCc7XG5pZiAocmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKSkge1xuICB2YXIgTElCUkFSWSA9IHJlcXVpcmUoJy4vX2xpYnJhcnknKTtcbiAgdmFyIGdsb2JhbCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpO1xuICB2YXIgZmFpbHMgPSByZXF1aXJlKCcuL19mYWlscycpO1xuICB2YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuICB2YXIgJHR5cGVkID0gcmVxdWlyZSgnLi9fdHlwZWQnKTtcbiAgdmFyICRidWZmZXIgPSByZXF1aXJlKCcuL190eXBlZC1idWZmZXInKTtcbiAgdmFyIGN0eCA9IHJlcXVpcmUoJy4vX2N0eCcpO1xuICB2YXIgYW5JbnN0YW5jZSA9IHJlcXVpcmUoJy4vX2FuLWluc3RhbmNlJyk7XG4gIHZhciBwcm9wZXJ0eURlc2MgPSByZXF1aXJlKCcuL19wcm9wZXJ0eS1kZXNjJyk7XG4gIHZhciBoaWRlID0gcmVxdWlyZSgnLi9faGlkZScpO1xuICB2YXIgcmVkZWZpbmVBbGwgPSByZXF1aXJlKCcuL19yZWRlZmluZS1hbGwnKTtcbiAgdmFyIHRvSW50ZWdlciA9IHJlcXVpcmUoJy4vX3RvLWludGVnZXInKTtcbiAgdmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG4gIHZhciB0b0luZGV4ID0gcmVxdWlyZSgnLi9fdG8taW5kZXgnKTtcbiAgdmFyIHRvQWJzb2x1dGVJbmRleCA9IHJlcXVpcmUoJy4vX3RvLWFic29sdXRlLWluZGV4Jyk7XG4gIHZhciB0b1ByaW1pdGl2ZSA9IHJlcXVpcmUoJy4vX3RvLXByaW1pdGl2ZScpO1xuICB2YXIgaGFzID0gcmVxdWlyZSgnLi9faGFzJyk7XG4gIHZhciBjbGFzc29mID0gcmVxdWlyZSgnLi9fY2xhc3NvZicpO1xuICB2YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcbiAgdmFyIHRvT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8tb2JqZWN0Jyk7XG4gIHZhciBpc0FycmF5SXRlciA9IHJlcXVpcmUoJy4vX2lzLWFycmF5LWl0ZXInKTtcbiAgdmFyIGNyZWF0ZSA9IHJlcXVpcmUoJy4vX29iamVjdC1jcmVhdGUnKTtcbiAgdmFyIGdldFByb3RvdHlwZU9mID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdwbycpO1xuICB2YXIgZ09QTiA9IHJlcXVpcmUoJy4vX29iamVjdC1nb3BuJykuZjtcbiAgdmFyIGdldEl0ZXJGbiA9IHJlcXVpcmUoJy4vY29yZS5nZXQtaXRlcmF0b3ItbWV0aG9kJyk7XG4gIHZhciB1aWQgPSByZXF1aXJlKCcuL191aWQnKTtcbiAgdmFyIHdrcyA9IHJlcXVpcmUoJy4vX3drcycpO1xuICB2YXIgY3JlYXRlQXJyYXlNZXRob2QgPSByZXF1aXJlKCcuL19hcnJheS1tZXRob2RzJyk7XG4gIHZhciBjcmVhdGVBcnJheUluY2x1ZGVzID0gcmVxdWlyZSgnLi9fYXJyYXktaW5jbHVkZXMnKTtcbiAgdmFyIHNwZWNpZXNDb25zdHJ1Y3RvciA9IHJlcXVpcmUoJy4vX3NwZWNpZXMtY29uc3RydWN0b3InKTtcbiAgdmFyIEFycmF5SXRlcmF0b3JzID0gcmVxdWlyZSgnLi9lczYuYXJyYXkuaXRlcmF0b3InKTtcbiAgdmFyIEl0ZXJhdG9ycyA9IHJlcXVpcmUoJy4vX2l0ZXJhdG9ycycpO1xuICB2YXIgJGl0ZXJEZXRlY3QgPSByZXF1aXJlKCcuL19pdGVyLWRldGVjdCcpO1xuICB2YXIgc2V0U3BlY2llcyA9IHJlcXVpcmUoJy4vX3NldC1zcGVjaWVzJyk7XG4gIHZhciBhcnJheUZpbGwgPSByZXF1aXJlKCcuL19hcnJheS1maWxsJyk7XG4gIHZhciBhcnJheUNvcHlXaXRoaW4gPSByZXF1aXJlKCcuL19hcnJheS1jb3B5LXdpdGhpbicpO1xuICB2YXIgJERQID0gcmVxdWlyZSgnLi9fb2JqZWN0LWRwJyk7XG4gIHZhciAkR09QRCA9IHJlcXVpcmUoJy4vX29iamVjdC1nb3BkJyk7XG4gIHZhciBkUCA9ICREUC5mO1xuICB2YXIgZ09QRCA9ICRHT1BELmY7XG4gIHZhciBSYW5nZUVycm9yID0gZ2xvYmFsLlJhbmdlRXJyb3I7XG4gIHZhciBUeXBlRXJyb3IgPSBnbG9iYWwuVHlwZUVycm9yO1xuICB2YXIgVWludDhBcnJheSA9IGdsb2JhbC5VaW50OEFycmF5O1xuICB2YXIgQVJSQVlfQlVGRkVSID0gJ0FycmF5QnVmZmVyJztcbiAgdmFyIFNIQVJFRF9CVUZGRVIgPSAnU2hhcmVkJyArIEFSUkFZX0JVRkZFUjtcbiAgdmFyIEJZVEVTX1BFUl9FTEVNRU5UID0gJ0JZVEVTX1BFUl9FTEVNRU5UJztcbiAgdmFyIFBST1RPVFlQRSA9ICdwcm90b3R5cGUnO1xuICB2YXIgQXJyYXlQcm90byA9IEFycmF5W1BST1RPVFlQRV07XG4gIHZhciAkQXJyYXlCdWZmZXIgPSAkYnVmZmVyLkFycmF5QnVmZmVyO1xuICB2YXIgJERhdGFWaWV3ID0gJGJ1ZmZlci5EYXRhVmlldztcbiAgdmFyIGFycmF5Rm9yRWFjaCA9IGNyZWF0ZUFycmF5TWV0aG9kKDApO1xuICB2YXIgYXJyYXlGaWx0ZXIgPSBjcmVhdGVBcnJheU1ldGhvZCgyKTtcbiAgdmFyIGFycmF5U29tZSA9IGNyZWF0ZUFycmF5TWV0aG9kKDMpO1xuICB2YXIgYXJyYXlFdmVyeSA9IGNyZWF0ZUFycmF5TWV0aG9kKDQpO1xuICB2YXIgYXJyYXlGaW5kID0gY3JlYXRlQXJyYXlNZXRob2QoNSk7XG4gIHZhciBhcnJheUZpbmRJbmRleCA9IGNyZWF0ZUFycmF5TWV0aG9kKDYpO1xuICB2YXIgYXJyYXlJbmNsdWRlcyA9IGNyZWF0ZUFycmF5SW5jbHVkZXModHJ1ZSk7XG4gIHZhciBhcnJheUluZGV4T2YgPSBjcmVhdGVBcnJheUluY2x1ZGVzKGZhbHNlKTtcbiAgdmFyIGFycmF5VmFsdWVzID0gQXJyYXlJdGVyYXRvcnMudmFsdWVzO1xuICB2YXIgYXJyYXlLZXlzID0gQXJyYXlJdGVyYXRvcnMua2V5cztcbiAgdmFyIGFycmF5RW50cmllcyA9IEFycmF5SXRlcmF0b3JzLmVudHJpZXM7XG4gIHZhciBhcnJheUxhc3RJbmRleE9mID0gQXJyYXlQcm90by5sYXN0SW5kZXhPZjtcbiAgdmFyIGFycmF5UmVkdWNlID0gQXJyYXlQcm90by5yZWR1Y2U7XG4gIHZhciBhcnJheVJlZHVjZVJpZ2h0ID0gQXJyYXlQcm90by5yZWR1Y2VSaWdodDtcbiAgdmFyIGFycmF5Sm9pbiA9IEFycmF5UHJvdG8uam9pbjtcbiAgdmFyIGFycmF5U29ydCA9IEFycmF5UHJvdG8uc29ydDtcbiAgdmFyIGFycmF5U2xpY2UgPSBBcnJheVByb3RvLnNsaWNlO1xuICB2YXIgYXJyYXlUb1N0cmluZyA9IEFycmF5UHJvdG8udG9TdHJpbmc7XG4gIHZhciBhcnJheVRvTG9jYWxlU3RyaW5nID0gQXJyYXlQcm90by50b0xvY2FsZVN0cmluZztcbiAgdmFyIElURVJBVE9SID0gd2tzKCdpdGVyYXRvcicpO1xuICB2YXIgVEFHID0gd2tzKCd0b1N0cmluZ1RhZycpO1xuICB2YXIgVFlQRURfQ09OU1RSVUNUT1IgPSB1aWQoJ3R5cGVkX2NvbnN0cnVjdG9yJyk7XG4gIHZhciBERUZfQ09OU1RSVUNUT1IgPSB1aWQoJ2RlZl9jb25zdHJ1Y3RvcicpO1xuICB2YXIgQUxMX0NPTlNUUlVDVE9SUyA9ICR0eXBlZC5DT05TVFI7XG4gIHZhciBUWVBFRF9BUlJBWSA9ICR0eXBlZC5UWVBFRDtcbiAgdmFyIFZJRVcgPSAkdHlwZWQuVklFVztcbiAgdmFyIFdST05HX0xFTkdUSCA9ICdXcm9uZyBsZW5ndGghJztcblxuICB2YXIgJG1hcCA9IGNyZWF0ZUFycmF5TWV0aG9kKDEsIGZ1bmN0aW9uIChPLCBsZW5ndGgpIHtcbiAgICByZXR1cm4gYWxsb2NhdGUoc3BlY2llc0NvbnN0cnVjdG9yKE8sIE9bREVGX0NPTlNUUlVDVE9SXSksIGxlbmd0aCk7XG4gIH0pO1xuXG4gIHZhciBMSVRUTEVfRU5ESUFOID0gZmFpbHMoZnVuY3Rpb24gKCkge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bmRlZlxuICAgIHJldHVybiBuZXcgVWludDhBcnJheShuZXcgVWludDE2QXJyYXkoWzFdKS5idWZmZXIpWzBdID09PSAxO1xuICB9KTtcblxuICB2YXIgRk9SQ0VEX1NFVCA9ICEhVWludDhBcnJheSAmJiAhIVVpbnQ4QXJyYXlbUFJPVE9UWVBFXS5zZXQgJiYgZmFpbHMoZnVuY3Rpb24gKCkge1xuICAgIG5ldyBVaW50OEFycmF5KDEpLnNldCh7fSk7XG4gIH0pO1xuXG4gIHZhciB0b09mZnNldCA9IGZ1bmN0aW9uIChpdCwgQllURVMpIHtcbiAgICB2YXIgb2Zmc2V0ID0gdG9JbnRlZ2VyKGl0KTtcbiAgICBpZiAob2Zmc2V0IDwgMCB8fCBvZmZzZXQgJSBCWVRFUykgdGhyb3cgUmFuZ2VFcnJvcignV3Jvbmcgb2Zmc2V0IScpO1xuICAgIHJldHVybiBvZmZzZXQ7XG4gIH07XG5cbiAgdmFyIHZhbGlkYXRlID0gZnVuY3Rpb24gKGl0KSB7XG4gICAgaWYgKGlzT2JqZWN0KGl0KSAmJiBUWVBFRF9BUlJBWSBpbiBpdCkgcmV0dXJuIGl0O1xuICAgIHRocm93IFR5cGVFcnJvcihpdCArICcgaXMgbm90IGEgdHlwZWQgYXJyYXkhJyk7XG4gIH07XG5cbiAgdmFyIGFsbG9jYXRlID0gZnVuY3Rpb24gKEMsIGxlbmd0aCkge1xuICAgIGlmICghKGlzT2JqZWN0KEMpICYmIFRZUEVEX0NPTlNUUlVDVE9SIGluIEMpKSB7XG4gICAgICB0aHJvdyBUeXBlRXJyb3IoJ0l0IGlzIG5vdCBhIHR5cGVkIGFycmF5IGNvbnN0cnVjdG9yIScpO1xuICAgIH0gcmV0dXJuIG5ldyBDKGxlbmd0aCk7XG4gIH07XG5cbiAgdmFyIHNwZWNpZXNGcm9tTGlzdCA9IGZ1bmN0aW9uIChPLCBsaXN0KSB7XG4gICAgcmV0dXJuIGZyb21MaXN0KHNwZWNpZXNDb25zdHJ1Y3RvcihPLCBPW0RFRl9DT05TVFJVQ1RPUl0pLCBsaXN0KTtcbiAgfTtcblxuICB2YXIgZnJvbUxpc3QgPSBmdW5jdGlvbiAoQywgbGlzdCkge1xuICAgIHZhciBpbmRleCA9IDA7XG4gICAgdmFyIGxlbmd0aCA9IGxpc3QubGVuZ3RoO1xuICAgIHZhciByZXN1bHQgPSBhbGxvY2F0ZShDLCBsZW5ndGgpO1xuICAgIHdoaWxlIChsZW5ndGggPiBpbmRleCkgcmVzdWx0W2luZGV4XSA9IGxpc3RbaW5kZXgrK107XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfTtcblxuICB2YXIgYWRkR2V0dGVyID0gZnVuY3Rpb24gKGl0LCBrZXksIGludGVybmFsKSB7XG4gICAgZFAoaXQsIGtleSwgeyBnZXQ6IGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXMuX2RbaW50ZXJuYWxdOyB9IH0pO1xuICB9O1xuXG4gIHZhciAkZnJvbSA9IGZ1bmN0aW9uIGZyb20oc291cmNlIC8qICwgbWFwZm4sIHRoaXNBcmcgKi8pIHtcbiAgICB2YXIgTyA9IHRvT2JqZWN0KHNvdXJjZSk7XG4gICAgdmFyIGFMZW4gPSBhcmd1bWVudHMubGVuZ3RoO1xuICAgIHZhciBtYXBmbiA9IGFMZW4gPiAxID8gYXJndW1lbnRzWzFdIDogdW5kZWZpbmVkO1xuICAgIHZhciBtYXBwaW5nID0gbWFwZm4gIT09IHVuZGVmaW5lZDtcbiAgICB2YXIgaXRlckZuID0gZ2V0SXRlckZuKE8pO1xuICAgIHZhciBpLCBsZW5ndGgsIHZhbHVlcywgcmVzdWx0LCBzdGVwLCBpdGVyYXRvcjtcbiAgICBpZiAoaXRlckZuICE9IHVuZGVmaW5lZCAmJiAhaXNBcnJheUl0ZXIoaXRlckZuKSkge1xuICAgICAgZm9yIChpdGVyYXRvciA9IGl0ZXJGbi5jYWxsKE8pLCB2YWx1ZXMgPSBbXSwgaSA9IDA7ICEoc3RlcCA9IGl0ZXJhdG9yLm5leHQoKSkuZG9uZTsgaSsrKSB7XG4gICAgICAgIHZhbHVlcy5wdXNoKHN0ZXAudmFsdWUpO1xuICAgICAgfSBPID0gdmFsdWVzO1xuICAgIH1cbiAgICBpZiAobWFwcGluZyAmJiBhTGVuID4gMikgbWFwZm4gPSBjdHgobWFwZm4sIGFyZ3VtZW50c1syXSwgMik7XG4gICAgZm9yIChpID0gMCwgbGVuZ3RoID0gdG9MZW5ndGgoTy5sZW5ndGgpLCByZXN1bHQgPSBhbGxvY2F0ZSh0aGlzLCBsZW5ndGgpOyBsZW5ndGggPiBpOyBpKyspIHtcbiAgICAgIHJlc3VsdFtpXSA9IG1hcHBpbmcgPyBtYXBmbihPW2ldLCBpKSA6IE9baV07XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH07XG5cbiAgdmFyICRvZiA9IGZ1bmN0aW9uIG9mKC8qIC4uLml0ZW1zICovKSB7XG4gICAgdmFyIGluZGV4ID0gMDtcbiAgICB2YXIgbGVuZ3RoID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgICB2YXIgcmVzdWx0ID0gYWxsb2NhdGUodGhpcywgbGVuZ3RoKTtcbiAgICB3aGlsZSAobGVuZ3RoID4gaW5kZXgpIHJlc3VsdFtpbmRleF0gPSBhcmd1bWVudHNbaW5kZXgrK107XG4gICAgcmV0dXJuIHJlc3VsdDtcbiAgfTtcblxuICAvLyBpT1MgU2FmYXJpIDYueCBmYWlscyBoZXJlXG4gIHZhciBUT19MT0NBTEVfQlVHID0gISFVaW50OEFycmF5ICYmIGZhaWxzKGZ1bmN0aW9uICgpIHsgYXJyYXlUb0xvY2FsZVN0cmluZy5jYWxsKG5ldyBVaW50OEFycmF5KDEpKTsgfSk7XG5cbiAgdmFyICR0b0xvY2FsZVN0cmluZyA9IGZ1bmN0aW9uIHRvTG9jYWxlU3RyaW5nKCkge1xuICAgIHJldHVybiBhcnJheVRvTG9jYWxlU3RyaW5nLmFwcGx5KFRPX0xPQ0FMRV9CVUcgPyBhcnJheVNsaWNlLmNhbGwodmFsaWRhdGUodGhpcykpIDogdmFsaWRhdGUodGhpcyksIGFyZ3VtZW50cyk7XG4gIH07XG5cbiAgdmFyIHByb3RvID0ge1xuICAgIGNvcHlXaXRoaW46IGZ1bmN0aW9uIGNvcHlXaXRoaW4odGFyZ2V0LCBzdGFydCAvKiAsIGVuZCAqLykge1xuICAgICAgcmV0dXJuIGFycmF5Q29weVdpdGhpbi5jYWxsKHZhbGlkYXRlKHRoaXMpLCB0YXJnZXQsIHN0YXJ0LCBhcmd1bWVudHMubGVuZ3RoID4gMiA/IGFyZ3VtZW50c1syXSA6IHVuZGVmaW5lZCk7XG4gICAgfSxcbiAgICBldmVyeTogZnVuY3Rpb24gZXZlcnkoY2FsbGJhY2tmbiAvKiAsIHRoaXNBcmcgKi8pIHtcbiAgICAgIHJldHVybiBhcnJheUV2ZXJ5KHZhbGlkYXRlKHRoaXMpLCBjYWxsYmFja2ZuLCBhcmd1bWVudHMubGVuZ3RoID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCk7XG4gICAgfSxcbiAgICBmaWxsOiBmdW5jdGlvbiBmaWxsKHZhbHVlIC8qICwgc3RhcnQsIGVuZCAqLykgeyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVudXNlZC12YXJzXG4gICAgICByZXR1cm4gYXJyYXlGaWxsLmFwcGx5KHZhbGlkYXRlKHRoaXMpLCBhcmd1bWVudHMpO1xuICAgIH0sXG4gICAgZmlsdGVyOiBmdW5jdGlvbiBmaWx0ZXIoY2FsbGJhY2tmbiAvKiAsIHRoaXNBcmcgKi8pIHtcbiAgICAgIHJldHVybiBzcGVjaWVzRnJvbUxpc3QodGhpcywgYXJyYXlGaWx0ZXIodmFsaWRhdGUodGhpcyksIGNhbGxiYWNrZm4sXG4gICAgICAgIGFyZ3VtZW50cy5sZW5ndGggPiAxID8gYXJndW1lbnRzWzFdIDogdW5kZWZpbmVkKSk7XG4gICAgfSxcbiAgICBmaW5kOiBmdW5jdGlvbiBmaW5kKHByZWRpY2F0ZSAvKiAsIHRoaXNBcmcgKi8pIHtcbiAgICAgIHJldHVybiBhcnJheUZpbmQodmFsaWRhdGUodGhpcyksIHByZWRpY2F0ZSwgYXJndW1lbnRzLmxlbmd0aCA+IDEgPyBhcmd1bWVudHNbMV0gOiB1bmRlZmluZWQpO1xuICAgIH0sXG4gICAgZmluZEluZGV4OiBmdW5jdGlvbiBmaW5kSW5kZXgocHJlZGljYXRlIC8qICwgdGhpc0FyZyAqLykge1xuICAgICAgcmV0dXJuIGFycmF5RmluZEluZGV4KHZhbGlkYXRlKHRoaXMpLCBwcmVkaWNhdGUsIGFyZ3VtZW50cy5sZW5ndGggPiAxID8gYXJndW1lbnRzWzFdIDogdW5kZWZpbmVkKTtcbiAgICB9LFxuICAgIGZvckVhY2g6IGZ1bmN0aW9uIGZvckVhY2goY2FsbGJhY2tmbiAvKiAsIHRoaXNBcmcgKi8pIHtcbiAgICAgIGFycmF5Rm9yRWFjaCh2YWxpZGF0ZSh0aGlzKSwgY2FsbGJhY2tmbiwgYXJndW1lbnRzLmxlbmd0aCA+IDEgPyBhcmd1bWVudHNbMV0gOiB1bmRlZmluZWQpO1xuICAgIH0sXG4gICAgaW5kZXhPZjogZnVuY3Rpb24gaW5kZXhPZihzZWFyY2hFbGVtZW50IC8qICwgZnJvbUluZGV4ICovKSB7XG4gICAgICByZXR1cm4gYXJyYXlJbmRleE9mKHZhbGlkYXRlKHRoaXMpLCBzZWFyY2hFbGVtZW50LCBhcmd1bWVudHMubGVuZ3RoID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCk7XG4gICAgfSxcbiAgICBpbmNsdWRlczogZnVuY3Rpb24gaW5jbHVkZXMoc2VhcmNoRWxlbWVudCAvKiAsIGZyb21JbmRleCAqLykge1xuICAgICAgcmV0dXJuIGFycmF5SW5jbHVkZXModmFsaWRhdGUodGhpcyksIHNlYXJjaEVsZW1lbnQsIGFyZ3VtZW50cy5sZW5ndGggPiAxID8gYXJndW1lbnRzWzFdIDogdW5kZWZpbmVkKTtcbiAgICB9LFxuICAgIGpvaW46IGZ1bmN0aW9uIGpvaW4oc2VwYXJhdG9yKSB7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICAgIHJldHVybiBhcnJheUpvaW4uYXBwbHkodmFsaWRhdGUodGhpcyksIGFyZ3VtZW50cyk7XG4gICAgfSxcbiAgICBsYXN0SW5kZXhPZjogZnVuY3Rpb24gbGFzdEluZGV4T2Yoc2VhcmNoRWxlbWVudCAvKiAsIGZyb21JbmRleCAqLykgeyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLXVudXNlZC12YXJzXG4gICAgICByZXR1cm4gYXJyYXlMYXN0SW5kZXhPZi5hcHBseSh2YWxpZGF0ZSh0aGlzKSwgYXJndW1lbnRzKTtcbiAgICB9LFxuICAgIG1hcDogZnVuY3Rpb24gbWFwKG1hcGZuIC8qICwgdGhpc0FyZyAqLykge1xuICAgICAgcmV0dXJuICRtYXAodmFsaWRhdGUodGhpcyksIG1hcGZuLCBhcmd1bWVudHMubGVuZ3RoID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCk7XG4gICAgfSxcbiAgICByZWR1Y2U6IGZ1bmN0aW9uIHJlZHVjZShjYWxsYmFja2ZuIC8qICwgaW5pdGlhbFZhbHVlICovKSB7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICAgIHJldHVybiBhcnJheVJlZHVjZS5hcHBseSh2YWxpZGF0ZSh0aGlzKSwgYXJndW1lbnRzKTtcbiAgICB9LFxuICAgIHJlZHVjZVJpZ2h0OiBmdW5jdGlvbiByZWR1Y2VSaWdodChjYWxsYmFja2ZuIC8qICwgaW5pdGlhbFZhbHVlICovKSB7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICAgIHJldHVybiBhcnJheVJlZHVjZVJpZ2h0LmFwcGx5KHZhbGlkYXRlKHRoaXMpLCBhcmd1bWVudHMpO1xuICAgIH0sXG4gICAgcmV2ZXJzZTogZnVuY3Rpb24gcmV2ZXJzZSgpIHtcbiAgICAgIHZhciB0aGF0ID0gdGhpcztcbiAgICAgIHZhciBsZW5ndGggPSB2YWxpZGF0ZSh0aGF0KS5sZW5ndGg7XG4gICAgICB2YXIgbWlkZGxlID0gTWF0aC5mbG9vcihsZW5ndGggLyAyKTtcbiAgICAgIHZhciBpbmRleCA9IDA7XG4gICAgICB2YXIgdmFsdWU7XG4gICAgICB3aGlsZSAoaW5kZXggPCBtaWRkbGUpIHtcbiAgICAgICAgdmFsdWUgPSB0aGF0W2luZGV4XTtcbiAgICAgICAgdGhhdFtpbmRleCsrXSA9IHRoYXRbLS1sZW5ndGhdO1xuICAgICAgICB0aGF0W2xlbmd0aF0gPSB2YWx1ZTtcbiAgICAgIH0gcmV0dXJuIHRoYXQ7XG4gICAgfSxcbiAgICBzb21lOiBmdW5jdGlvbiBzb21lKGNhbGxiYWNrZm4gLyogLCB0aGlzQXJnICovKSB7XG4gICAgICByZXR1cm4gYXJyYXlTb21lKHZhbGlkYXRlKHRoaXMpLCBjYWxsYmFja2ZuLCBhcmd1bWVudHMubGVuZ3RoID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCk7XG4gICAgfSxcbiAgICBzb3J0OiBmdW5jdGlvbiBzb3J0KGNvbXBhcmVmbikge1xuICAgICAgcmV0dXJuIGFycmF5U29ydC5jYWxsKHZhbGlkYXRlKHRoaXMpLCBjb21wYXJlZm4pO1xuICAgIH0sXG4gICAgc3ViYXJyYXk6IGZ1bmN0aW9uIHN1YmFycmF5KGJlZ2luLCBlbmQpIHtcbiAgICAgIHZhciBPID0gdmFsaWRhdGUodGhpcyk7XG4gICAgICB2YXIgbGVuZ3RoID0gTy5sZW5ndGg7XG4gICAgICB2YXIgJGJlZ2luID0gdG9BYnNvbHV0ZUluZGV4KGJlZ2luLCBsZW5ndGgpO1xuICAgICAgcmV0dXJuIG5ldyAoc3BlY2llc0NvbnN0cnVjdG9yKE8sIE9bREVGX0NPTlNUUlVDVE9SXSkpKFxuICAgICAgICBPLmJ1ZmZlcixcbiAgICAgICAgTy5ieXRlT2Zmc2V0ICsgJGJlZ2luICogTy5CWVRFU19QRVJfRUxFTUVOVCxcbiAgICAgICAgdG9MZW5ndGgoKGVuZCA9PT0gdW5kZWZpbmVkID8gbGVuZ3RoIDogdG9BYnNvbHV0ZUluZGV4KGVuZCwgbGVuZ3RoKSkgLSAkYmVnaW4pXG4gICAgICApO1xuICAgIH1cbiAgfTtcblxuICB2YXIgJHNsaWNlID0gZnVuY3Rpb24gc2xpY2Uoc3RhcnQsIGVuZCkge1xuICAgIHJldHVybiBzcGVjaWVzRnJvbUxpc3QodGhpcywgYXJyYXlTbGljZS5jYWxsKHZhbGlkYXRlKHRoaXMpLCBzdGFydCwgZW5kKSk7XG4gIH07XG5cbiAgdmFyICRzZXQgPSBmdW5jdGlvbiBzZXQoYXJyYXlMaWtlIC8qICwgb2Zmc2V0ICovKSB7XG4gICAgdmFsaWRhdGUodGhpcyk7XG4gICAgdmFyIG9mZnNldCA9IHRvT2Zmc2V0KGFyZ3VtZW50c1sxXSwgMSk7XG4gICAgdmFyIGxlbmd0aCA9IHRoaXMubGVuZ3RoO1xuICAgIHZhciBzcmMgPSB0b09iamVjdChhcnJheUxpa2UpO1xuICAgIHZhciBsZW4gPSB0b0xlbmd0aChzcmMubGVuZ3RoKTtcbiAgICB2YXIgaW5kZXggPSAwO1xuICAgIGlmIChsZW4gKyBvZmZzZXQgPiBsZW5ndGgpIHRocm93IFJhbmdlRXJyb3IoV1JPTkdfTEVOR1RIKTtcbiAgICB3aGlsZSAoaW5kZXggPCBsZW4pIHRoaXNbb2Zmc2V0ICsgaW5kZXhdID0gc3JjW2luZGV4KytdO1xuICB9O1xuXG4gIHZhciAkaXRlcmF0b3JzID0ge1xuICAgIGVudHJpZXM6IGZ1bmN0aW9uIGVudHJpZXMoKSB7XG4gICAgICByZXR1cm4gYXJyYXlFbnRyaWVzLmNhbGwodmFsaWRhdGUodGhpcykpO1xuICAgIH0sXG4gICAga2V5czogZnVuY3Rpb24ga2V5cygpIHtcbiAgICAgIHJldHVybiBhcnJheUtleXMuY2FsbCh2YWxpZGF0ZSh0aGlzKSk7XG4gICAgfSxcbiAgICB2YWx1ZXM6IGZ1bmN0aW9uIHZhbHVlcygpIHtcbiAgICAgIHJldHVybiBhcnJheVZhbHVlcy5jYWxsKHZhbGlkYXRlKHRoaXMpKTtcbiAgICB9XG4gIH07XG5cbiAgdmFyIGlzVEFJbmRleCA9IGZ1bmN0aW9uICh0YXJnZXQsIGtleSkge1xuICAgIHJldHVybiBpc09iamVjdCh0YXJnZXQpXG4gICAgICAmJiB0YXJnZXRbVFlQRURfQVJSQVldXG4gICAgICAmJiB0eXBlb2Yga2V5ICE9ICdzeW1ib2wnXG4gICAgICAmJiBrZXkgaW4gdGFyZ2V0XG4gICAgICAmJiBTdHJpbmcoK2tleSkgPT0gU3RyaW5nKGtleSk7XG4gIH07XG4gIHZhciAkZ2V0RGVzYyA9IGZ1bmN0aW9uIGdldE93blByb3BlcnR5RGVzY3JpcHRvcih0YXJnZXQsIGtleSkge1xuICAgIHJldHVybiBpc1RBSW5kZXgodGFyZ2V0LCBrZXkgPSB0b1ByaW1pdGl2ZShrZXksIHRydWUpKVxuICAgICAgPyBwcm9wZXJ0eURlc2MoMiwgdGFyZ2V0W2tleV0pXG4gICAgICA6IGdPUEQodGFyZ2V0LCBrZXkpO1xuICB9O1xuICB2YXIgJHNldERlc2MgPSBmdW5jdGlvbiBkZWZpbmVQcm9wZXJ0eSh0YXJnZXQsIGtleSwgZGVzYykge1xuICAgIGlmIChpc1RBSW5kZXgodGFyZ2V0LCBrZXkgPSB0b1ByaW1pdGl2ZShrZXksIHRydWUpKVxuICAgICAgJiYgaXNPYmplY3QoZGVzYylcbiAgICAgICYmIGhhcyhkZXNjLCAndmFsdWUnKVxuICAgICAgJiYgIWhhcyhkZXNjLCAnZ2V0JylcbiAgICAgICYmICFoYXMoZGVzYywgJ3NldCcpXG4gICAgICAvLyBUT0RPOiBhZGQgdmFsaWRhdGlvbiBkZXNjcmlwdG9yIHcvbyBjYWxsaW5nIGFjY2Vzc29yc1xuICAgICAgJiYgIWRlc2MuY29uZmlndXJhYmxlXG4gICAgICAmJiAoIWhhcyhkZXNjLCAnd3JpdGFibGUnKSB8fCBkZXNjLndyaXRhYmxlKVxuICAgICAgJiYgKCFoYXMoZGVzYywgJ2VudW1lcmFibGUnKSB8fCBkZXNjLmVudW1lcmFibGUpXG4gICAgKSB7XG4gICAgICB0YXJnZXRba2V5XSA9IGRlc2MudmFsdWU7XG4gICAgICByZXR1cm4gdGFyZ2V0O1xuICAgIH0gcmV0dXJuIGRQKHRhcmdldCwga2V5LCBkZXNjKTtcbiAgfTtcblxuICBpZiAoIUFMTF9DT05TVFJVQ1RPUlMpIHtcbiAgICAkR09QRC5mID0gJGdldERlc2M7XG4gICAgJERQLmYgPSAkc2V0RGVzYztcbiAgfVxuXG4gICRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogIUFMTF9DT05TVFJVQ1RPUlMsICdPYmplY3QnLCB7XG4gICAgZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yOiAkZ2V0RGVzYyxcbiAgICBkZWZpbmVQcm9wZXJ0eTogJHNldERlc2NcbiAgfSk7XG5cbiAgaWYgKGZhaWxzKGZ1bmN0aW9uICgpIHsgYXJyYXlUb1N0cmluZy5jYWxsKHt9KTsgfSkpIHtcbiAgICBhcnJheVRvU3RyaW5nID0gYXJyYXlUb0xvY2FsZVN0cmluZyA9IGZ1bmN0aW9uIHRvU3RyaW5nKCkge1xuICAgICAgcmV0dXJuIGFycmF5Sm9pbi5jYWxsKHRoaXMpO1xuICAgIH07XG4gIH1cblxuICB2YXIgJFR5cGVkQXJyYXlQcm90b3R5cGUkID0gcmVkZWZpbmVBbGwoe30sIHByb3RvKTtcbiAgcmVkZWZpbmVBbGwoJFR5cGVkQXJyYXlQcm90b3R5cGUkLCAkaXRlcmF0b3JzKTtcbiAgaGlkZSgkVHlwZWRBcnJheVByb3RvdHlwZSQsIElURVJBVE9SLCAkaXRlcmF0b3JzLnZhbHVlcyk7XG4gIHJlZGVmaW5lQWxsKCRUeXBlZEFycmF5UHJvdG90eXBlJCwge1xuICAgIHNsaWNlOiAkc2xpY2UsXG4gICAgc2V0OiAkc2V0LFxuICAgIGNvbnN0cnVjdG9yOiBmdW5jdGlvbiAoKSB7IC8qIG5vb3AgKi8gfSxcbiAgICB0b1N0cmluZzogYXJyYXlUb1N0cmluZyxcbiAgICB0b0xvY2FsZVN0cmluZzogJHRvTG9jYWxlU3RyaW5nXG4gIH0pO1xuICBhZGRHZXR0ZXIoJFR5cGVkQXJyYXlQcm90b3R5cGUkLCAnYnVmZmVyJywgJ2InKTtcbiAgYWRkR2V0dGVyKCRUeXBlZEFycmF5UHJvdG90eXBlJCwgJ2J5dGVPZmZzZXQnLCAnbycpO1xuICBhZGRHZXR0ZXIoJFR5cGVkQXJyYXlQcm90b3R5cGUkLCAnYnl0ZUxlbmd0aCcsICdsJyk7XG4gIGFkZEdldHRlcigkVHlwZWRBcnJheVByb3RvdHlwZSQsICdsZW5ndGgnLCAnZScpO1xuICBkUCgkVHlwZWRBcnJheVByb3RvdHlwZSQsIFRBRywge1xuICAgIGdldDogZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpc1tUWVBFRF9BUlJBWV07IH1cbiAgfSk7XG5cbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG1heC1zdGF0ZW1lbnRzXG4gIG1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKEtFWSwgQllURVMsIHdyYXBwZXIsIENMQU1QRUQpIHtcbiAgICBDTEFNUEVEID0gISFDTEFNUEVEO1xuICAgIHZhciBOQU1FID0gS0VZICsgKENMQU1QRUQgPyAnQ2xhbXBlZCcgOiAnJykgKyAnQXJyYXknO1xuICAgIHZhciBHRVRURVIgPSAnZ2V0JyArIEtFWTtcbiAgICB2YXIgU0VUVEVSID0gJ3NldCcgKyBLRVk7XG4gICAgdmFyIFR5cGVkQXJyYXkgPSBnbG9iYWxbTkFNRV07XG4gICAgdmFyIEJhc2UgPSBUeXBlZEFycmF5IHx8IHt9O1xuICAgIHZhciBUQUMgPSBUeXBlZEFycmF5ICYmIGdldFByb3RvdHlwZU9mKFR5cGVkQXJyYXkpO1xuICAgIHZhciBGT1JDRUQgPSAhVHlwZWRBcnJheSB8fCAhJHR5cGVkLkFCVjtcbiAgICB2YXIgTyA9IHt9O1xuICAgIHZhciBUeXBlZEFycmF5UHJvdG90eXBlID0gVHlwZWRBcnJheSAmJiBUeXBlZEFycmF5W1BST1RPVFlQRV07XG4gICAgdmFyIGdldHRlciA9IGZ1bmN0aW9uICh0aGF0LCBpbmRleCkge1xuICAgICAgdmFyIGRhdGEgPSB0aGF0Ll9kO1xuICAgICAgcmV0dXJuIGRhdGEudltHRVRURVJdKGluZGV4ICogQllURVMgKyBkYXRhLm8sIExJVFRMRV9FTkRJQU4pO1xuICAgIH07XG4gICAgdmFyIHNldHRlciA9IGZ1bmN0aW9uICh0aGF0LCBpbmRleCwgdmFsdWUpIHtcbiAgICAgIHZhciBkYXRhID0gdGhhdC5fZDtcbiAgICAgIGlmIChDTEFNUEVEKSB2YWx1ZSA9ICh2YWx1ZSA9IE1hdGgucm91bmQodmFsdWUpKSA8IDAgPyAwIDogdmFsdWUgPiAweGZmID8gMHhmZiA6IHZhbHVlICYgMHhmZjtcbiAgICAgIGRhdGEudltTRVRURVJdKGluZGV4ICogQllURVMgKyBkYXRhLm8sIHZhbHVlLCBMSVRUTEVfRU5ESUFOKTtcbiAgICB9O1xuICAgIHZhciBhZGRFbGVtZW50ID0gZnVuY3Rpb24gKHRoYXQsIGluZGV4KSB7XG4gICAgICBkUCh0aGF0LCBpbmRleCwge1xuICAgICAgICBnZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICAgICAgICByZXR1cm4gZ2V0dGVyKHRoaXMsIGluZGV4KTtcbiAgICAgICAgfSxcbiAgICAgICAgc2V0OiBmdW5jdGlvbiAodmFsdWUpIHtcbiAgICAgICAgICByZXR1cm4gc2V0dGVyKHRoaXMsIGluZGV4LCB2YWx1ZSk7XG4gICAgICAgIH0sXG4gICAgICAgIGVudW1lcmFibGU6IHRydWVcbiAgICAgIH0pO1xuICAgIH07XG4gICAgaWYgKEZPUkNFRCkge1xuICAgICAgVHlwZWRBcnJheSA9IHdyYXBwZXIoZnVuY3Rpb24gKHRoYXQsIGRhdGEsICRvZmZzZXQsICRsZW5ndGgpIHtcbiAgICAgICAgYW5JbnN0YW5jZSh0aGF0LCBUeXBlZEFycmF5LCBOQU1FLCAnX2QnKTtcbiAgICAgICAgdmFyIGluZGV4ID0gMDtcbiAgICAgICAgdmFyIG9mZnNldCA9IDA7XG4gICAgICAgIHZhciBidWZmZXIsIGJ5dGVMZW5ndGgsIGxlbmd0aCwga2xhc3M7XG4gICAgICAgIGlmICghaXNPYmplY3QoZGF0YSkpIHtcbiAgICAgICAgICBsZW5ndGggPSB0b0luZGV4KGRhdGEpO1xuICAgICAgICAgIGJ5dGVMZW5ndGggPSBsZW5ndGggKiBCWVRFUztcbiAgICAgICAgICBidWZmZXIgPSBuZXcgJEFycmF5QnVmZmVyKGJ5dGVMZW5ndGgpO1xuICAgICAgICB9IGVsc2UgaWYgKGRhdGEgaW5zdGFuY2VvZiAkQXJyYXlCdWZmZXIgfHwgKGtsYXNzID0gY2xhc3NvZihkYXRhKSkgPT0gQVJSQVlfQlVGRkVSIHx8IGtsYXNzID09IFNIQVJFRF9CVUZGRVIpIHtcbiAgICAgICAgICBidWZmZXIgPSBkYXRhO1xuICAgICAgICAgIG9mZnNldCA9IHRvT2Zmc2V0KCRvZmZzZXQsIEJZVEVTKTtcbiAgICAgICAgICB2YXIgJGxlbiA9IGRhdGEuYnl0ZUxlbmd0aDtcbiAgICAgICAgICBpZiAoJGxlbmd0aCA9PT0gdW5kZWZpbmVkKSB7XG4gICAgICAgICAgICBpZiAoJGxlbiAlIEJZVEVTKSB0aHJvdyBSYW5nZUVycm9yKFdST05HX0xFTkdUSCk7XG4gICAgICAgICAgICBieXRlTGVuZ3RoID0gJGxlbiAtIG9mZnNldDtcbiAgICAgICAgICAgIGlmIChieXRlTGVuZ3RoIDwgMCkgdGhyb3cgUmFuZ2VFcnJvcihXUk9OR19MRU5HVEgpO1xuICAgICAgICAgIH0gZWxzZSB7XG4gICAgICAgICAgICBieXRlTGVuZ3RoID0gdG9MZW5ndGgoJGxlbmd0aCkgKiBCWVRFUztcbiAgICAgICAgICAgIGlmIChieXRlTGVuZ3RoICsgb2Zmc2V0ID4gJGxlbikgdGhyb3cgUmFuZ2VFcnJvcihXUk9OR19MRU5HVEgpO1xuICAgICAgICAgIH1cbiAgICAgICAgICBsZW5ndGggPSBieXRlTGVuZ3RoIC8gQllURVM7XG4gICAgICAgIH0gZWxzZSBpZiAoVFlQRURfQVJSQVkgaW4gZGF0YSkge1xuICAgICAgICAgIHJldHVybiBmcm9tTGlzdChUeXBlZEFycmF5LCBkYXRhKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXR1cm4gJGZyb20uY2FsbChUeXBlZEFycmF5LCBkYXRhKTtcbiAgICAgICAgfVxuICAgICAgICBoaWRlKHRoYXQsICdfZCcsIHtcbiAgICAgICAgICBiOiBidWZmZXIsXG4gICAgICAgICAgbzogb2Zmc2V0LFxuICAgICAgICAgIGw6IGJ5dGVMZW5ndGgsXG4gICAgICAgICAgZTogbGVuZ3RoLFxuICAgICAgICAgIHY6IG5ldyAkRGF0YVZpZXcoYnVmZmVyKVxuICAgICAgICB9KTtcbiAgICAgICAgd2hpbGUgKGluZGV4IDwgbGVuZ3RoKSBhZGRFbGVtZW50KHRoYXQsIGluZGV4KyspO1xuICAgICAgfSk7XG4gICAgICBUeXBlZEFycmF5UHJvdG90eXBlID0gVHlwZWRBcnJheVtQUk9UT1RZUEVdID0gY3JlYXRlKCRUeXBlZEFycmF5UHJvdG90eXBlJCk7XG4gICAgICBoaWRlKFR5cGVkQXJyYXlQcm90b3R5cGUsICdjb25zdHJ1Y3RvcicsIFR5cGVkQXJyYXkpO1xuICAgIH0gZWxzZSBpZiAoIWZhaWxzKGZ1bmN0aW9uICgpIHtcbiAgICAgIFR5cGVkQXJyYXkoMSk7XG4gICAgfSkgfHwgIWZhaWxzKGZ1bmN0aW9uICgpIHtcbiAgICAgIG5ldyBUeXBlZEFycmF5KC0xKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1uZXdcbiAgICB9KSB8fCAhJGl0ZXJEZXRlY3QoZnVuY3Rpb24gKGl0ZXIpIHtcbiAgICAgIG5ldyBUeXBlZEFycmF5KCk7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tbmV3XG4gICAgICBuZXcgVHlwZWRBcnJheShudWxsKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1uZXdcbiAgICAgIG5ldyBUeXBlZEFycmF5KDEuNSk7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tbmV3XG4gICAgICBuZXcgVHlwZWRBcnJheShpdGVyKTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1uZXdcbiAgICB9LCB0cnVlKSkge1xuICAgICAgVHlwZWRBcnJheSA9IHdyYXBwZXIoZnVuY3Rpb24gKHRoYXQsIGRhdGEsICRvZmZzZXQsICRsZW5ndGgpIHtcbiAgICAgICAgYW5JbnN0YW5jZSh0aGF0LCBUeXBlZEFycmF5LCBOQU1FKTtcbiAgICAgICAgdmFyIGtsYXNzO1xuICAgICAgICAvLyBgd3NgIG1vZHVsZSBidWcsIHRlbXBvcmFyaWx5IHJlbW92ZSB2YWxpZGF0aW9uIGxlbmd0aCBmb3IgVWludDhBcnJheVxuICAgICAgICAvLyBodHRwczovL2dpdGh1Yi5jb20vd2Vic29ja2V0cy93cy9wdWxsLzY0NVxuICAgICAgICBpZiAoIWlzT2JqZWN0KGRhdGEpKSByZXR1cm4gbmV3IEJhc2UodG9JbmRleChkYXRhKSk7XG4gICAgICAgIGlmIChkYXRhIGluc3RhbmNlb2YgJEFycmF5QnVmZmVyIHx8IChrbGFzcyA9IGNsYXNzb2YoZGF0YSkpID09IEFSUkFZX0JVRkZFUiB8fCBrbGFzcyA9PSBTSEFSRURfQlVGRkVSKSB7XG4gICAgICAgICAgcmV0dXJuICRsZW5ndGggIT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgPyBuZXcgQmFzZShkYXRhLCB0b09mZnNldCgkb2Zmc2V0LCBCWVRFUyksICRsZW5ndGgpXG4gICAgICAgICAgICA6ICRvZmZzZXQgIT09IHVuZGVmaW5lZFxuICAgICAgICAgICAgICA/IG5ldyBCYXNlKGRhdGEsIHRvT2Zmc2V0KCRvZmZzZXQsIEJZVEVTKSlcbiAgICAgICAgICAgICAgOiBuZXcgQmFzZShkYXRhKTtcbiAgICAgICAgfVxuICAgICAgICBpZiAoVFlQRURfQVJSQVkgaW4gZGF0YSkgcmV0dXJuIGZyb21MaXN0KFR5cGVkQXJyYXksIGRhdGEpO1xuICAgICAgICByZXR1cm4gJGZyb20uY2FsbChUeXBlZEFycmF5LCBkYXRhKTtcbiAgICAgIH0pO1xuICAgICAgYXJyYXlGb3JFYWNoKFRBQyAhPT0gRnVuY3Rpb24ucHJvdG90eXBlID8gZ09QTihCYXNlKS5jb25jYXQoZ09QTihUQUMpKSA6IGdPUE4oQmFzZSksIGZ1bmN0aW9uIChrZXkpIHtcbiAgICAgICAgaWYgKCEoa2V5IGluIFR5cGVkQXJyYXkpKSBoaWRlKFR5cGVkQXJyYXksIGtleSwgQmFzZVtrZXldKTtcbiAgICAgIH0pO1xuICAgICAgVHlwZWRBcnJheVtQUk9UT1RZUEVdID0gVHlwZWRBcnJheVByb3RvdHlwZTtcbiAgICAgIGlmICghTElCUkFSWSkgVHlwZWRBcnJheVByb3RvdHlwZS5jb25zdHJ1Y3RvciA9IFR5cGVkQXJyYXk7XG4gICAgfVxuICAgIHZhciAkbmF0aXZlSXRlcmF0b3IgPSBUeXBlZEFycmF5UHJvdG90eXBlW0lURVJBVE9SXTtcbiAgICB2YXIgQ09SUkVDVF9JVEVSX05BTUUgPSAhISRuYXRpdmVJdGVyYXRvclxuICAgICAgJiYgKCRuYXRpdmVJdGVyYXRvci5uYW1lID09ICd2YWx1ZXMnIHx8ICRuYXRpdmVJdGVyYXRvci5uYW1lID09IHVuZGVmaW5lZCk7XG4gICAgdmFyICRpdGVyYXRvciA9ICRpdGVyYXRvcnMudmFsdWVzO1xuICAgIGhpZGUoVHlwZWRBcnJheSwgVFlQRURfQ09OU1RSVUNUT1IsIHRydWUpO1xuICAgIGhpZGUoVHlwZWRBcnJheVByb3RvdHlwZSwgVFlQRURfQVJSQVksIE5BTUUpO1xuICAgIGhpZGUoVHlwZWRBcnJheVByb3RvdHlwZSwgVklFVywgdHJ1ZSk7XG4gICAgaGlkZShUeXBlZEFycmF5UHJvdG90eXBlLCBERUZfQ09OU1RSVUNUT1IsIFR5cGVkQXJyYXkpO1xuXG4gICAgaWYgKENMQU1QRUQgPyBuZXcgVHlwZWRBcnJheSgxKVtUQUddICE9IE5BTUUgOiAhKFRBRyBpbiBUeXBlZEFycmF5UHJvdG90eXBlKSkge1xuICAgICAgZFAoVHlwZWRBcnJheVByb3RvdHlwZSwgVEFHLCB7XG4gICAgICAgIGdldDogZnVuY3Rpb24gKCkgeyByZXR1cm4gTkFNRTsgfVxuICAgICAgfSk7XG4gICAgfVxuXG4gICAgT1tOQU1FXSA9IFR5cGVkQXJyYXk7XG5cbiAgICAkZXhwb3J0KCRleHBvcnQuRyArICRleHBvcnQuVyArICRleHBvcnQuRiAqIChUeXBlZEFycmF5ICE9IEJhc2UpLCBPKTtcblxuICAgICRleHBvcnQoJGV4cG9ydC5TLCBOQU1FLCB7XG4gICAgICBCWVRFU19QRVJfRUxFTUVOVDogQllURVNcbiAgICB9KTtcblxuICAgICRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogZmFpbHMoZnVuY3Rpb24gKCkgeyBCYXNlLm9mLmNhbGwoVHlwZWRBcnJheSwgMSk7IH0pLCBOQU1FLCB7XG4gICAgICBmcm9tOiAkZnJvbSxcbiAgICAgIG9mOiAkb2ZcbiAgICB9KTtcblxuICAgIGlmICghKEJZVEVTX1BFUl9FTEVNRU5UIGluIFR5cGVkQXJyYXlQcm90b3R5cGUpKSBoaWRlKFR5cGVkQXJyYXlQcm90b3R5cGUsIEJZVEVTX1BFUl9FTEVNRU5ULCBCWVRFUyk7XG5cbiAgICAkZXhwb3J0KCRleHBvcnQuUCwgTkFNRSwgcHJvdG8pO1xuXG4gICAgc2V0U3BlY2llcyhOQU1FKTtcblxuICAgICRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogRk9SQ0VEX1NFVCwgTkFNRSwgeyBzZXQ6ICRzZXQgfSk7XG5cbiAgICAkZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqICFDT1JSRUNUX0lURVJfTkFNRSwgTkFNRSwgJGl0ZXJhdG9ycyk7XG5cbiAgICBpZiAoIUxJQlJBUlkgJiYgVHlwZWRBcnJheVByb3RvdHlwZS50b1N0cmluZyAhPSBhcnJheVRvU3RyaW5nKSBUeXBlZEFycmF5UHJvdG90eXBlLnRvU3RyaW5nID0gYXJyYXlUb1N0cmluZztcblxuICAgICRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogZmFpbHMoZnVuY3Rpb24gKCkge1xuICAgICAgbmV3IFR5cGVkQXJyYXkoMSkuc2xpY2UoKTtcbiAgICB9KSwgTkFNRSwgeyBzbGljZTogJHNsaWNlIH0pO1xuXG4gICAgJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAoZmFpbHMoZnVuY3Rpb24gKCkge1xuICAgICAgcmV0dXJuIFsxLCAyXS50b0xvY2FsZVN0cmluZygpICE9IG5ldyBUeXBlZEFycmF5KFsxLCAyXSkudG9Mb2NhbGVTdHJpbmcoKTtcbiAgICB9KSB8fCAhZmFpbHMoZnVuY3Rpb24gKCkge1xuICAgICAgVHlwZWRBcnJheVByb3RvdHlwZS50b0xvY2FsZVN0cmluZy5jYWxsKFsxLCAyXSk7XG4gICAgfSkpLCBOQU1FLCB7IHRvTG9jYWxlU3RyaW5nOiAkdG9Mb2NhbGVTdHJpbmcgfSk7XG5cbiAgICBJdGVyYXRvcnNbTkFNRV0gPSBDT1JSRUNUX0lURVJfTkFNRSA/ICRuYXRpdmVJdGVyYXRvciA6ICRpdGVyYXRvcjtcbiAgICBpZiAoIUxJQlJBUlkgJiYgIUNPUlJFQ1RfSVRFUl9OQU1FKSBoaWRlKFR5cGVkQXJyYXlQcm90b3R5cGUsIElURVJBVE9SLCAkaXRlcmF0b3IpO1xuICB9O1xufSBlbHNlIG1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKCkgeyAvKiBlbXB0eSAqLyB9O1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIGdsb2JhbCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpO1xudmFyIERFU0NSSVBUT1JTID0gcmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKTtcbnZhciBMSUJSQVJZID0gcmVxdWlyZSgnLi9fbGlicmFyeScpO1xudmFyICR0eXBlZCA9IHJlcXVpcmUoJy4vX3R5cGVkJyk7XG52YXIgaGlkZSA9IHJlcXVpcmUoJy4vX2hpZGUnKTtcbnZhciByZWRlZmluZUFsbCA9IHJlcXVpcmUoJy4vX3JlZGVmaW5lLWFsbCcpO1xudmFyIGZhaWxzID0gcmVxdWlyZSgnLi9fZmFpbHMnKTtcbnZhciBhbkluc3RhbmNlID0gcmVxdWlyZSgnLi9fYW4taW5zdGFuY2UnKTtcbnZhciB0b0ludGVnZXIgPSByZXF1aXJlKCcuL190by1pbnRlZ2VyJyk7XG52YXIgdG9MZW5ndGggPSByZXF1aXJlKCcuL190by1sZW5ndGgnKTtcbnZhciB0b0luZGV4ID0gcmVxdWlyZSgnLi9fdG8taW5kZXgnKTtcbnZhciBnT1BOID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcG4nKS5mO1xudmFyIGRQID0gcmVxdWlyZSgnLi9fb2JqZWN0LWRwJykuZjtcbnZhciBhcnJheUZpbGwgPSByZXF1aXJlKCcuL19hcnJheS1maWxsJyk7XG52YXIgc2V0VG9TdHJpbmdUYWcgPSByZXF1aXJlKCcuL19zZXQtdG8tc3RyaW5nLXRhZycpO1xudmFyIEFSUkFZX0JVRkZFUiA9ICdBcnJheUJ1ZmZlcic7XG52YXIgREFUQV9WSUVXID0gJ0RhdGFWaWV3JztcbnZhciBQUk9UT1RZUEUgPSAncHJvdG90eXBlJztcbnZhciBXUk9OR19MRU5HVEggPSAnV3JvbmcgbGVuZ3RoISc7XG52YXIgV1JPTkdfSU5ERVggPSAnV3JvbmcgaW5kZXghJztcbnZhciAkQXJyYXlCdWZmZXIgPSBnbG9iYWxbQVJSQVlfQlVGRkVSXTtcbnZhciAkRGF0YVZpZXcgPSBnbG9iYWxbREFUQV9WSUVXXTtcbnZhciBNYXRoID0gZ2xvYmFsLk1hdGg7XG52YXIgUmFuZ2VFcnJvciA9IGdsb2JhbC5SYW5nZUVycm9yO1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXNoYWRvdy1yZXN0cmljdGVkLW5hbWVzXG52YXIgSW5maW5pdHkgPSBnbG9iYWwuSW5maW5pdHk7XG52YXIgQmFzZUJ1ZmZlciA9ICRBcnJheUJ1ZmZlcjtcbnZhciBhYnMgPSBNYXRoLmFicztcbnZhciBwb3cgPSBNYXRoLnBvdztcbnZhciBmbG9vciA9IE1hdGguZmxvb3I7XG52YXIgbG9nID0gTWF0aC5sb2c7XG52YXIgTE4yID0gTWF0aC5MTjI7XG52YXIgQlVGRkVSID0gJ2J1ZmZlcic7XG52YXIgQllURV9MRU5HVEggPSAnYnl0ZUxlbmd0aCc7XG52YXIgQllURV9PRkZTRVQgPSAnYnl0ZU9mZnNldCc7XG52YXIgJEJVRkZFUiA9IERFU0NSSVBUT1JTID8gJ19iJyA6IEJVRkZFUjtcbnZhciAkTEVOR1RIID0gREVTQ1JJUFRPUlMgPyAnX2wnIDogQllURV9MRU5HVEg7XG52YXIgJE9GRlNFVCA9IERFU0NSSVBUT1JTID8gJ19vJyA6IEJZVEVfT0ZGU0VUO1xuXG4vLyBJRUVFNzU0IGNvbnZlcnNpb25zIGJhc2VkIG9uIGh0dHBzOi8vZ2l0aHViLmNvbS9mZXJvc3MvaWVlZTc1NFxuZnVuY3Rpb24gcGFja0lFRUU3NTQodmFsdWUsIG1MZW4sIG5CeXRlcykge1xuICB2YXIgYnVmZmVyID0gbmV3IEFycmF5KG5CeXRlcyk7XG4gIHZhciBlTGVuID0gbkJ5dGVzICogOCAtIG1MZW4gLSAxO1xuICB2YXIgZU1heCA9ICgxIDw8IGVMZW4pIC0gMTtcbiAgdmFyIGVCaWFzID0gZU1heCA+PiAxO1xuICB2YXIgcnQgPSBtTGVuID09PSAyMyA/IHBvdygyLCAtMjQpIC0gcG93KDIsIC03NykgOiAwO1xuICB2YXIgaSA9IDA7XG4gIHZhciBzID0gdmFsdWUgPCAwIHx8IHZhbHVlID09PSAwICYmIDEgLyB2YWx1ZSA8IDAgPyAxIDogMDtcbiAgdmFyIGUsIG0sIGM7XG4gIHZhbHVlID0gYWJzKHZhbHVlKTtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXNlbGYtY29tcGFyZVxuICBpZiAodmFsdWUgIT0gdmFsdWUgfHwgdmFsdWUgPT09IEluZmluaXR5KSB7XG4gICAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXNlbGYtY29tcGFyZVxuICAgIG0gPSB2YWx1ZSAhPSB2YWx1ZSA/IDEgOiAwO1xuICAgIGUgPSBlTWF4O1xuICB9IGVsc2Uge1xuICAgIGUgPSBmbG9vcihsb2codmFsdWUpIC8gTE4yKTtcbiAgICBpZiAodmFsdWUgKiAoYyA9IHBvdygyLCAtZSkpIDwgMSkge1xuICAgICAgZS0tO1xuICAgICAgYyAqPSAyO1xuICAgIH1cbiAgICBpZiAoZSArIGVCaWFzID49IDEpIHtcbiAgICAgIHZhbHVlICs9IHJ0IC8gYztcbiAgICB9IGVsc2Uge1xuICAgICAgdmFsdWUgKz0gcnQgKiBwb3coMiwgMSAtIGVCaWFzKTtcbiAgICB9XG4gICAgaWYgKHZhbHVlICogYyA+PSAyKSB7XG4gICAgICBlKys7XG4gICAgICBjIC89IDI7XG4gICAgfVxuICAgIGlmIChlICsgZUJpYXMgPj0gZU1heCkge1xuICAgICAgbSA9IDA7XG4gICAgICBlID0gZU1heDtcbiAgICB9IGVsc2UgaWYgKGUgKyBlQmlhcyA+PSAxKSB7XG4gICAgICBtID0gKHZhbHVlICogYyAtIDEpICogcG93KDIsIG1MZW4pO1xuICAgICAgZSA9IGUgKyBlQmlhcztcbiAgICB9IGVsc2Uge1xuICAgICAgbSA9IHZhbHVlICogcG93KDIsIGVCaWFzIC0gMSkgKiBwb3coMiwgbUxlbik7XG4gICAgICBlID0gMDtcbiAgICB9XG4gIH1cbiAgZm9yICg7IG1MZW4gPj0gODsgYnVmZmVyW2krK10gPSBtICYgMjU1LCBtIC89IDI1NiwgbUxlbiAtPSA4KTtcbiAgZSA9IGUgPDwgbUxlbiB8IG07XG4gIGVMZW4gKz0gbUxlbjtcbiAgZm9yICg7IGVMZW4gPiAwOyBidWZmZXJbaSsrXSA9IGUgJiAyNTUsIGUgLz0gMjU2LCBlTGVuIC09IDgpO1xuICBidWZmZXJbLS1pXSB8PSBzICogMTI4O1xuICByZXR1cm4gYnVmZmVyO1xufVxuZnVuY3Rpb24gdW5wYWNrSUVFRTc1NChidWZmZXIsIG1MZW4sIG5CeXRlcykge1xuICB2YXIgZUxlbiA9IG5CeXRlcyAqIDggLSBtTGVuIC0gMTtcbiAgdmFyIGVNYXggPSAoMSA8PCBlTGVuKSAtIDE7XG4gIHZhciBlQmlhcyA9IGVNYXggPj4gMTtcbiAgdmFyIG5CaXRzID0gZUxlbiAtIDc7XG4gIHZhciBpID0gbkJ5dGVzIC0gMTtcbiAgdmFyIHMgPSBidWZmZXJbaS0tXTtcbiAgdmFyIGUgPSBzICYgMTI3O1xuICB2YXIgbTtcbiAgcyA+Pj0gNztcbiAgZm9yICg7IG5CaXRzID4gMDsgZSA9IGUgKiAyNTYgKyBidWZmZXJbaV0sIGktLSwgbkJpdHMgLT0gOCk7XG4gIG0gPSBlICYgKDEgPDwgLW5CaXRzKSAtIDE7XG4gIGUgPj49IC1uQml0cztcbiAgbkJpdHMgKz0gbUxlbjtcbiAgZm9yICg7IG5CaXRzID4gMDsgbSA9IG0gKiAyNTYgKyBidWZmZXJbaV0sIGktLSwgbkJpdHMgLT0gOCk7XG4gIGlmIChlID09PSAwKSB7XG4gICAgZSA9IDEgLSBlQmlhcztcbiAgfSBlbHNlIGlmIChlID09PSBlTWF4KSB7XG4gICAgcmV0dXJuIG0gPyBOYU4gOiBzID8gLUluZmluaXR5IDogSW5maW5pdHk7XG4gIH0gZWxzZSB7XG4gICAgbSA9IG0gKyBwb3coMiwgbUxlbik7XG4gICAgZSA9IGUgLSBlQmlhcztcbiAgfSByZXR1cm4gKHMgPyAtMSA6IDEpICogbSAqIHBvdygyLCBlIC0gbUxlbik7XG59XG5cbmZ1bmN0aW9uIHVucGFja0kzMihieXRlcykge1xuICByZXR1cm4gYnl0ZXNbM10gPDwgMjQgfCBieXRlc1syXSA8PCAxNiB8IGJ5dGVzWzFdIDw8IDggfCBieXRlc1swXTtcbn1cbmZ1bmN0aW9uIHBhY2tJOChpdCkge1xuICByZXR1cm4gW2l0ICYgMHhmZl07XG59XG5mdW5jdGlvbiBwYWNrSTE2KGl0KSB7XG4gIHJldHVybiBbaXQgJiAweGZmLCBpdCA+PiA4ICYgMHhmZl07XG59XG5mdW5jdGlvbiBwYWNrSTMyKGl0KSB7XG4gIHJldHVybiBbaXQgJiAweGZmLCBpdCA+PiA4ICYgMHhmZiwgaXQgPj4gMTYgJiAweGZmLCBpdCA+PiAyNCAmIDB4ZmZdO1xufVxuZnVuY3Rpb24gcGFja0Y2NChpdCkge1xuICByZXR1cm4gcGFja0lFRUU3NTQoaXQsIDUyLCA4KTtcbn1cbmZ1bmN0aW9uIHBhY2tGMzIoaXQpIHtcbiAgcmV0dXJuIHBhY2tJRUVFNzU0KGl0LCAyMywgNCk7XG59XG5cbmZ1bmN0aW9uIGFkZEdldHRlcihDLCBrZXksIGludGVybmFsKSB7XG4gIGRQKENbUFJPVE9UWVBFXSwga2V5LCB7IGdldDogZnVuY3Rpb24gKCkgeyByZXR1cm4gdGhpc1tpbnRlcm5hbF07IH0gfSk7XG59XG5cbmZ1bmN0aW9uIGdldCh2aWV3LCBieXRlcywgaW5kZXgsIGlzTGl0dGxlRW5kaWFuKSB7XG4gIHZhciBudW1JbmRleCA9ICtpbmRleDtcbiAgdmFyIGludEluZGV4ID0gdG9JbmRleChudW1JbmRleCk7XG4gIGlmIChpbnRJbmRleCArIGJ5dGVzID4gdmlld1skTEVOR1RIXSkgdGhyb3cgUmFuZ2VFcnJvcihXUk9OR19JTkRFWCk7XG4gIHZhciBzdG9yZSA9IHZpZXdbJEJVRkZFUl0uX2I7XG4gIHZhciBzdGFydCA9IGludEluZGV4ICsgdmlld1skT0ZGU0VUXTtcbiAgdmFyIHBhY2sgPSBzdG9yZS5zbGljZShzdGFydCwgc3RhcnQgKyBieXRlcyk7XG4gIHJldHVybiBpc0xpdHRsZUVuZGlhbiA/IHBhY2sgOiBwYWNrLnJldmVyc2UoKTtcbn1cbmZ1bmN0aW9uIHNldCh2aWV3LCBieXRlcywgaW5kZXgsIGNvbnZlcnNpb24sIHZhbHVlLCBpc0xpdHRsZUVuZGlhbikge1xuICB2YXIgbnVtSW5kZXggPSAraW5kZXg7XG4gIHZhciBpbnRJbmRleCA9IHRvSW5kZXgobnVtSW5kZXgpO1xuICBpZiAoaW50SW5kZXggKyBieXRlcyA+IHZpZXdbJExFTkdUSF0pIHRocm93IFJhbmdlRXJyb3IoV1JPTkdfSU5ERVgpO1xuICB2YXIgc3RvcmUgPSB2aWV3WyRCVUZGRVJdLl9iO1xuICB2YXIgc3RhcnQgPSBpbnRJbmRleCArIHZpZXdbJE9GRlNFVF07XG4gIHZhciBwYWNrID0gY29udmVyc2lvbigrdmFsdWUpO1xuICBmb3IgKHZhciBpID0gMDsgaSA8IGJ5dGVzOyBpKyspIHN0b3JlW3N0YXJ0ICsgaV0gPSBwYWNrW2lzTGl0dGxlRW5kaWFuID8gaSA6IGJ5dGVzIC0gaSAtIDFdO1xufVxuXG5pZiAoISR0eXBlZC5BQlYpIHtcbiAgJEFycmF5QnVmZmVyID0gZnVuY3Rpb24gQXJyYXlCdWZmZXIobGVuZ3RoKSB7XG4gICAgYW5JbnN0YW5jZSh0aGlzLCAkQXJyYXlCdWZmZXIsIEFSUkFZX0JVRkZFUik7XG4gICAgdmFyIGJ5dGVMZW5ndGggPSB0b0luZGV4KGxlbmd0aCk7XG4gICAgdGhpcy5fYiA9IGFycmF5RmlsbC5jYWxsKG5ldyBBcnJheShieXRlTGVuZ3RoKSwgMCk7XG4gICAgdGhpc1skTEVOR1RIXSA9IGJ5dGVMZW5ndGg7XG4gIH07XG5cbiAgJERhdGFWaWV3ID0gZnVuY3Rpb24gRGF0YVZpZXcoYnVmZmVyLCBieXRlT2Zmc2V0LCBieXRlTGVuZ3RoKSB7XG4gICAgYW5JbnN0YW5jZSh0aGlzLCAkRGF0YVZpZXcsIERBVEFfVklFVyk7XG4gICAgYW5JbnN0YW5jZShidWZmZXIsICRBcnJheUJ1ZmZlciwgREFUQV9WSUVXKTtcbiAgICB2YXIgYnVmZmVyTGVuZ3RoID0gYnVmZmVyWyRMRU5HVEhdO1xuICAgIHZhciBvZmZzZXQgPSB0b0ludGVnZXIoYnl0ZU9mZnNldCk7XG4gICAgaWYgKG9mZnNldCA8IDAgfHwgb2Zmc2V0ID4gYnVmZmVyTGVuZ3RoKSB0aHJvdyBSYW5nZUVycm9yKCdXcm9uZyBvZmZzZXQhJyk7XG4gICAgYnl0ZUxlbmd0aCA9IGJ5dGVMZW5ndGggPT09IHVuZGVmaW5lZCA/IGJ1ZmZlckxlbmd0aCAtIG9mZnNldCA6IHRvTGVuZ3RoKGJ5dGVMZW5ndGgpO1xuICAgIGlmIChvZmZzZXQgKyBieXRlTGVuZ3RoID4gYnVmZmVyTGVuZ3RoKSB0aHJvdyBSYW5nZUVycm9yKFdST05HX0xFTkdUSCk7XG4gICAgdGhpc1skQlVGRkVSXSA9IGJ1ZmZlcjtcbiAgICB0aGlzWyRPRkZTRVRdID0gb2Zmc2V0O1xuICAgIHRoaXNbJExFTkdUSF0gPSBieXRlTGVuZ3RoO1xuICB9O1xuXG4gIGlmIChERVNDUklQVE9SUykge1xuICAgIGFkZEdldHRlcigkQXJyYXlCdWZmZXIsIEJZVEVfTEVOR1RILCAnX2wnKTtcbiAgICBhZGRHZXR0ZXIoJERhdGFWaWV3LCBCVUZGRVIsICdfYicpO1xuICAgIGFkZEdldHRlcigkRGF0YVZpZXcsIEJZVEVfTEVOR1RILCAnX2wnKTtcbiAgICBhZGRHZXR0ZXIoJERhdGFWaWV3LCBCWVRFX09GRlNFVCwgJ19vJyk7XG4gIH1cblxuICByZWRlZmluZUFsbCgkRGF0YVZpZXdbUFJPVE9UWVBFXSwge1xuICAgIGdldEludDg6IGZ1bmN0aW9uIGdldEludDgoYnl0ZU9mZnNldCkge1xuICAgICAgcmV0dXJuIGdldCh0aGlzLCAxLCBieXRlT2Zmc2V0KVswXSA8PCAyNCA+PiAyNDtcbiAgICB9LFxuICAgIGdldFVpbnQ4OiBmdW5jdGlvbiBnZXRVaW50OChieXRlT2Zmc2V0KSB7XG4gICAgICByZXR1cm4gZ2V0KHRoaXMsIDEsIGJ5dGVPZmZzZXQpWzBdO1xuICAgIH0sXG4gICAgZ2V0SW50MTY6IGZ1bmN0aW9uIGdldEludDE2KGJ5dGVPZmZzZXQgLyogLCBsaXR0bGVFbmRpYW4gKi8pIHtcbiAgICAgIHZhciBieXRlcyA9IGdldCh0aGlzLCAyLCBieXRlT2Zmc2V0LCBhcmd1bWVudHNbMV0pO1xuICAgICAgcmV0dXJuIChieXRlc1sxXSA8PCA4IHwgYnl0ZXNbMF0pIDw8IDE2ID4+IDE2O1xuICAgIH0sXG4gICAgZ2V0VWludDE2OiBmdW5jdGlvbiBnZXRVaW50MTYoYnl0ZU9mZnNldCAvKiAsIGxpdHRsZUVuZGlhbiAqLykge1xuICAgICAgdmFyIGJ5dGVzID0gZ2V0KHRoaXMsIDIsIGJ5dGVPZmZzZXQsIGFyZ3VtZW50c1sxXSk7XG4gICAgICByZXR1cm4gYnl0ZXNbMV0gPDwgOCB8IGJ5dGVzWzBdO1xuICAgIH0sXG4gICAgZ2V0SW50MzI6IGZ1bmN0aW9uIGdldEludDMyKGJ5dGVPZmZzZXQgLyogLCBsaXR0bGVFbmRpYW4gKi8pIHtcbiAgICAgIHJldHVybiB1bnBhY2tJMzIoZ2V0KHRoaXMsIDQsIGJ5dGVPZmZzZXQsIGFyZ3VtZW50c1sxXSkpO1xuICAgIH0sXG4gICAgZ2V0VWludDMyOiBmdW5jdGlvbiBnZXRVaW50MzIoYnl0ZU9mZnNldCAvKiAsIGxpdHRsZUVuZGlhbiAqLykge1xuICAgICAgcmV0dXJuIHVucGFja0kzMihnZXQodGhpcywgNCwgYnl0ZU9mZnNldCwgYXJndW1lbnRzWzFdKSkgPj4+IDA7XG4gICAgfSxcbiAgICBnZXRGbG9hdDMyOiBmdW5jdGlvbiBnZXRGbG9hdDMyKGJ5dGVPZmZzZXQgLyogLCBsaXR0bGVFbmRpYW4gKi8pIHtcbiAgICAgIHJldHVybiB1bnBhY2tJRUVFNzU0KGdldCh0aGlzLCA0LCBieXRlT2Zmc2V0LCBhcmd1bWVudHNbMV0pLCAyMywgNCk7XG4gICAgfSxcbiAgICBnZXRGbG9hdDY0OiBmdW5jdGlvbiBnZXRGbG9hdDY0KGJ5dGVPZmZzZXQgLyogLCBsaXR0bGVFbmRpYW4gKi8pIHtcbiAgICAgIHJldHVybiB1bnBhY2tJRUVFNzU0KGdldCh0aGlzLCA4LCBieXRlT2Zmc2V0LCBhcmd1bWVudHNbMV0pLCA1MiwgOCk7XG4gICAgfSxcbiAgICBzZXRJbnQ4OiBmdW5jdGlvbiBzZXRJbnQ4KGJ5dGVPZmZzZXQsIHZhbHVlKSB7XG4gICAgICBzZXQodGhpcywgMSwgYnl0ZU9mZnNldCwgcGFja0k4LCB2YWx1ZSk7XG4gICAgfSxcbiAgICBzZXRVaW50ODogZnVuY3Rpb24gc2V0VWludDgoYnl0ZU9mZnNldCwgdmFsdWUpIHtcbiAgICAgIHNldCh0aGlzLCAxLCBieXRlT2Zmc2V0LCBwYWNrSTgsIHZhbHVlKTtcbiAgICB9LFxuICAgIHNldEludDE2OiBmdW5jdGlvbiBzZXRJbnQxNihieXRlT2Zmc2V0LCB2YWx1ZSAvKiAsIGxpdHRsZUVuZGlhbiAqLykge1xuICAgICAgc2V0KHRoaXMsIDIsIGJ5dGVPZmZzZXQsIHBhY2tJMTYsIHZhbHVlLCBhcmd1bWVudHNbMl0pO1xuICAgIH0sXG4gICAgc2V0VWludDE2OiBmdW5jdGlvbiBzZXRVaW50MTYoYnl0ZU9mZnNldCwgdmFsdWUgLyogLCBsaXR0bGVFbmRpYW4gKi8pIHtcbiAgICAgIHNldCh0aGlzLCAyLCBieXRlT2Zmc2V0LCBwYWNrSTE2LCB2YWx1ZSwgYXJndW1lbnRzWzJdKTtcbiAgICB9LFxuICAgIHNldEludDMyOiBmdW5jdGlvbiBzZXRJbnQzMihieXRlT2Zmc2V0LCB2YWx1ZSAvKiAsIGxpdHRsZUVuZGlhbiAqLykge1xuICAgICAgc2V0KHRoaXMsIDQsIGJ5dGVPZmZzZXQsIHBhY2tJMzIsIHZhbHVlLCBhcmd1bWVudHNbMl0pO1xuICAgIH0sXG4gICAgc2V0VWludDMyOiBmdW5jdGlvbiBzZXRVaW50MzIoYnl0ZU9mZnNldCwgdmFsdWUgLyogLCBsaXR0bGVFbmRpYW4gKi8pIHtcbiAgICAgIHNldCh0aGlzLCA0LCBieXRlT2Zmc2V0LCBwYWNrSTMyLCB2YWx1ZSwgYXJndW1lbnRzWzJdKTtcbiAgICB9LFxuICAgIHNldEZsb2F0MzI6IGZ1bmN0aW9uIHNldEZsb2F0MzIoYnl0ZU9mZnNldCwgdmFsdWUgLyogLCBsaXR0bGVFbmRpYW4gKi8pIHtcbiAgICAgIHNldCh0aGlzLCA0LCBieXRlT2Zmc2V0LCBwYWNrRjMyLCB2YWx1ZSwgYXJndW1lbnRzWzJdKTtcbiAgICB9LFxuICAgIHNldEZsb2F0NjQ6IGZ1bmN0aW9uIHNldEZsb2F0NjQoYnl0ZU9mZnNldCwgdmFsdWUgLyogLCBsaXR0bGVFbmRpYW4gKi8pIHtcbiAgICAgIHNldCh0aGlzLCA4LCBieXRlT2Zmc2V0LCBwYWNrRjY0LCB2YWx1ZSwgYXJndW1lbnRzWzJdKTtcbiAgICB9XG4gIH0pO1xufSBlbHNlIHtcbiAgaWYgKCFmYWlscyhmdW5jdGlvbiAoKSB7XG4gICAgJEFycmF5QnVmZmVyKDEpO1xuICB9KSB8fCAhZmFpbHMoZnVuY3Rpb24gKCkge1xuICAgIG5ldyAkQXJyYXlCdWZmZXIoLTEpOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLW5ld1xuICB9KSB8fCBmYWlscyhmdW5jdGlvbiAoKSB7XG4gICAgbmV3ICRBcnJheUJ1ZmZlcigpOyAvLyBlc2xpbnQtZGlzYWJsZS1saW5lIG5vLW5ld1xuICAgIG5ldyAkQXJyYXlCdWZmZXIoMS41KTsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby1uZXdcbiAgICBuZXcgJEFycmF5QnVmZmVyKE5hTik7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tbmV3XG4gICAgcmV0dXJuICRBcnJheUJ1ZmZlci5uYW1lICE9IEFSUkFZX0JVRkZFUjtcbiAgfSkpIHtcbiAgICAkQXJyYXlCdWZmZXIgPSBmdW5jdGlvbiBBcnJheUJ1ZmZlcihsZW5ndGgpIHtcbiAgICAgIGFuSW5zdGFuY2UodGhpcywgJEFycmF5QnVmZmVyKTtcbiAgICAgIHJldHVybiBuZXcgQmFzZUJ1ZmZlcih0b0luZGV4KGxlbmd0aCkpO1xuICAgIH07XG4gICAgdmFyIEFycmF5QnVmZmVyUHJvdG8gPSAkQXJyYXlCdWZmZXJbUFJPVE9UWVBFXSA9IEJhc2VCdWZmZXJbUFJPVE9UWVBFXTtcbiAgICBmb3IgKHZhciBrZXlzID0gZ09QTihCYXNlQnVmZmVyKSwgaiA9IDAsIGtleTsga2V5cy5sZW5ndGggPiBqOykge1xuICAgICAgaWYgKCEoKGtleSA9IGtleXNbaisrXSkgaW4gJEFycmF5QnVmZmVyKSkgaGlkZSgkQXJyYXlCdWZmZXIsIGtleSwgQmFzZUJ1ZmZlcltrZXldKTtcbiAgICB9XG4gICAgaWYgKCFMSUJSQVJZKSBBcnJheUJ1ZmZlclByb3RvLmNvbnN0cnVjdG9yID0gJEFycmF5QnVmZmVyO1xuICB9XG4gIC8vIGlPUyBTYWZhcmkgNy54IGJ1Z1xuICB2YXIgdmlldyA9IG5ldyAkRGF0YVZpZXcobmV3ICRBcnJheUJ1ZmZlcigyKSk7XG4gIHZhciAkc2V0SW50OCA9ICREYXRhVmlld1tQUk9UT1RZUEVdLnNldEludDg7XG4gIHZpZXcuc2V0SW50OCgwLCAyMTQ3NDgzNjQ4KTtcbiAgdmlldy5zZXRJbnQ4KDEsIDIxNDc0ODM2NDkpO1xuICBpZiAodmlldy5nZXRJbnQ4KDApIHx8ICF2aWV3LmdldEludDgoMSkpIHJlZGVmaW5lQWxsKCREYXRhVmlld1tQUk9UT1RZUEVdLCB7XG4gICAgc2V0SW50ODogZnVuY3Rpb24gc2V0SW50OChieXRlT2Zmc2V0LCB2YWx1ZSkge1xuICAgICAgJHNldEludDguY2FsbCh0aGlzLCBieXRlT2Zmc2V0LCB2YWx1ZSA8PCAyNCA+PiAyNCk7XG4gICAgfSxcbiAgICBzZXRVaW50ODogZnVuY3Rpb24gc2V0VWludDgoYnl0ZU9mZnNldCwgdmFsdWUpIHtcbiAgICAgICRzZXRJbnQ4LmNhbGwodGhpcywgYnl0ZU9mZnNldCwgdmFsdWUgPDwgMjQgPj4gMjQpO1xuICAgIH1cbiAgfSwgdHJ1ZSk7XG59XG5zZXRUb1N0cmluZ1RhZygkQXJyYXlCdWZmZXIsIEFSUkFZX0JVRkZFUik7XG5zZXRUb1N0cmluZ1RhZygkRGF0YVZpZXcsIERBVEFfVklFVyk7XG5oaWRlKCREYXRhVmlld1tQUk9UT1RZUEVdLCAkdHlwZWQuVklFVywgdHJ1ZSk7XG5leHBvcnRzW0FSUkFZX0JVRkZFUl0gPSAkQXJyYXlCdWZmZXI7XG5leHBvcnRzW0RBVEFfVklFV10gPSAkRGF0YVZpZXc7XG4iLCJ2YXIgZ2xvYmFsID0gcmVxdWlyZSgnLi9fZ2xvYmFsJyk7XG52YXIgaGlkZSA9IHJlcXVpcmUoJy4vX2hpZGUnKTtcbnZhciB1aWQgPSByZXF1aXJlKCcuL191aWQnKTtcbnZhciBUWVBFRCA9IHVpZCgndHlwZWRfYXJyYXknKTtcbnZhciBWSUVXID0gdWlkKCd2aWV3Jyk7XG52YXIgQUJWID0gISEoZ2xvYmFsLkFycmF5QnVmZmVyICYmIGdsb2JhbC5EYXRhVmlldyk7XG52YXIgQ09OU1RSID0gQUJWO1xudmFyIGkgPSAwO1xudmFyIGwgPSA5O1xudmFyIFR5cGVkO1xuXG52YXIgVHlwZWRBcnJheUNvbnN0cnVjdG9ycyA9IChcbiAgJ0ludDhBcnJheSxVaW50OEFycmF5LFVpbnQ4Q2xhbXBlZEFycmF5LEludDE2QXJyYXksVWludDE2QXJyYXksSW50MzJBcnJheSxVaW50MzJBcnJheSxGbG9hdDMyQXJyYXksRmxvYXQ2NEFycmF5J1xuKS5zcGxpdCgnLCcpO1xuXG53aGlsZSAoaSA8IGwpIHtcbiAgaWYgKFR5cGVkID0gZ2xvYmFsW1R5cGVkQXJyYXlDb25zdHJ1Y3RvcnNbaSsrXV0pIHtcbiAgICBoaWRlKFR5cGVkLnByb3RvdHlwZSwgVFlQRUQsIHRydWUpO1xuICAgIGhpZGUoVHlwZWQucHJvdG90eXBlLCBWSUVXLCB0cnVlKTtcbiAgfSBlbHNlIENPTlNUUiA9IGZhbHNlO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IHtcbiAgQUJWOiBBQlYsXG4gIENPTlNUUjogQ09OU1RSLFxuICBUWVBFRDogVFlQRUQsXG4gIFZJRVc6IFZJRVdcbn07XG4iLCJ2YXIgaWQgPSAwO1xudmFyIHB4ID0gTWF0aC5yYW5kb20oKTtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKGtleSkge1xuICByZXR1cm4gJ1N5bWJvbCgnLmNvbmNhdChrZXkgPT09IHVuZGVmaW5lZCA/ICcnIDoga2V5LCAnKV8nLCAoKytpZCArIHB4KS50b1N0cmluZygzNikpO1xufTtcbiIsInZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciBuYXZpZ2F0b3IgPSBnbG9iYWwubmF2aWdhdG9yO1xuXG5tb2R1bGUuZXhwb3J0cyA9IG5hdmlnYXRvciAmJiBuYXZpZ2F0b3IudXNlckFnZW50IHx8ICcnO1xuIiwidmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG5tb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChpdCwgVFlQRSkge1xuICBpZiAoIWlzT2JqZWN0KGl0KSB8fCBpdC5fdCAhPT0gVFlQRSkgdGhyb3cgVHlwZUVycm9yKCdJbmNvbXBhdGlibGUgcmVjZWl2ZXIsICcgKyBUWVBFICsgJyByZXF1aXJlZCEnKTtcbiAgcmV0dXJuIGl0O1xufTtcbiIsInZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciBjb3JlID0gcmVxdWlyZSgnLi9fY29yZScpO1xudmFyIExJQlJBUlkgPSByZXF1aXJlKCcuL19saWJyYXJ5Jyk7XG52YXIgd2tzRXh0ID0gcmVxdWlyZSgnLi9fd2tzLWV4dCcpO1xudmFyIGRlZmluZVByb3BlcnR5ID0gcmVxdWlyZSgnLi9fb2JqZWN0LWRwJykuZjtcbm1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gKG5hbWUpIHtcbiAgdmFyICRTeW1ib2wgPSBjb3JlLlN5bWJvbCB8fCAoY29yZS5TeW1ib2wgPSBMSUJSQVJZID8ge30gOiBnbG9iYWwuU3ltYm9sIHx8IHt9KTtcbiAgaWYgKG5hbWUuY2hhckF0KDApICE9ICdfJyAmJiAhKG5hbWUgaW4gJFN5bWJvbCkpIGRlZmluZVByb3BlcnR5KCRTeW1ib2wsIG5hbWUsIHsgdmFsdWU6IHdrc0V4dC5mKG5hbWUpIH0pO1xufTtcbiIsImV4cG9ydHMuZiA9IHJlcXVpcmUoJy4vX3drcycpO1xuIiwidmFyIHN0b3JlID0gcmVxdWlyZSgnLi9fc2hhcmVkJykoJ3drcycpO1xudmFyIHVpZCA9IHJlcXVpcmUoJy4vX3VpZCcpO1xudmFyIFN5bWJvbCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpLlN5bWJvbDtcbnZhciBVU0VfU1lNQk9MID0gdHlwZW9mIFN5bWJvbCA9PSAnZnVuY3Rpb24nO1xuXG52YXIgJGV4cG9ydHMgPSBtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIChuYW1lKSB7XG4gIHJldHVybiBzdG9yZVtuYW1lXSB8fCAoc3RvcmVbbmFtZV0gPVxuICAgIFVTRV9TWU1CT0wgJiYgU3ltYm9sW25hbWVdIHx8IChVU0VfU1lNQk9MID8gU3ltYm9sIDogdWlkKSgnU3ltYm9sLicgKyBuYW1lKSk7XG59O1xuXG4kZXhwb3J0cy5zdG9yZSA9IHN0b3JlO1xuIiwidmFyIGNsYXNzb2YgPSByZXF1aXJlKCcuL19jbGFzc29mJyk7XG52YXIgSVRFUkFUT1IgPSByZXF1aXJlKCcuL193a3MnKSgnaXRlcmF0b3InKTtcbnZhciBJdGVyYXRvcnMgPSByZXF1aXJlKCcuL19pdGVyYXRvcnMnKTtcbm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZSgnLi9fY29yZScpLmdldEl0ZXJhdG9yTWV0aG9kID0gZnVuY3Rpb24gKGl0KSB7XG4gIGlmIChpdCAhPSB1bmRlZmluZWQpIHJldHVybiBpdFtJVEVSQVRPUl1cbiAgICB8fCBpdFsnQEBpdGVyYXRvciddXG4gICAgfHwgSXRlcmF0b3JzW2NsYXNzb2YoaXQpXTtcbn07XG4iLCIvLyBodHRwczovL2dpdGh1Yi5jb20vYmVuamFtaW5nci9SZXhFeHAuZXNjYXBlXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRyZSA9IHJlcXVpcmUoJy4vX3JlcGxhY2VyJykoL1tcXFxcXiQqKz8uKCl8W1xcXXt9XS9nLCAnXFxcXCQmJyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnUmVnRXhwJywgeyBlc2NhcGU6IGZ1bmN0aW9uIGVzY2FwZShpdCkgeyByZXR1cm4gJHJlKGl0KTsgfSB9KTtcbiIsIi8vIDIyLjEuMy4zIEFycmF5LnByb3RvdHlwZS5jb3B5V2l0aGluKHRhcmdldCwgc3RhcnQsIGVuZCA9IHRoaXMubGVuZ3RoKVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlAsICdBcnJheScsIHsgY29weVdpdGhpbjogcmVxdWlyZSgnLi9fYXJyYXktY29weS13aXRoaW4nKSB9KTtcblxucmVxdWlyZSgnLi9fYWRkLXRvLXVuc2NvcGFibGVzJykoJ2NvcHlXaXRoaW4nKTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgJGV2ZXJ5ID0gcmVxdWlyZSgnLi9fYXJyYXktbWV0aG9kcycpKDQpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqICFyZXF1aXJlKCcuL19zdHJpY3QtbWV0aG9kJykoW10uZXZlcnksIHRydWUpLCAnQXJyYXknLCB7XG4gIC8vIDIyLjEuMy41IC8gMTUuNC40LjE2IEFycmF5LnByb3RvdHlwZS5ldmVyeShjYWxsYmFja2ZuIFssIHRoaXNBcmddKVxuICBldmVyeTogZnVuY3Rpb24gZXZlcnkoY2FsbGJhY2tmbiAvKiAsIHRoaXNBcmcgKi8pIHtcbiAgICByZXR1cm4gJGV2ZXJ5KHRoaXMsIGNhbGxiYWNrZm4sIGFyZ3VtZW50c1sxXSk7XG4gIH1cbn0pO1xuIiwiLy8gMjIuMS4zLjYgQXJyYXkucHJvdG90eXBlLmZpbGwodmFsdWUsIHN0YXJ0ID0gMCwgZW5kID0gdGhpcy5sZW5ndGgpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCwgJ0FycmF5JywgeyBmaWxsOiByZXF1aXJlKCcuL19hcnJheS1maWxsJykgfSk7XG5cbnJlcXVpcmUoJy4vX2FkZC10by11bnNjb3BhYmxlcycpKCdmaWxsJyk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRmaWx0ZXIgPSByZXF1aXJlKCcuL19hcnJheS1tZXRob2RzJykoMik7XG5cbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogIXJlcXVpcmUoJy4vX3N0cmljdC1tZXRob2QnKShbXS5maWx0ZXIsIHRydWUpLCAnQXJyYXknLCB7XG4gIC8vIDIyLjEuMy43IC8gMTUuNC40LjIwIEFycmF5LnByb3RvdHlwZS5maWx0ZXIoY2FsbGJhY2tmbiBbLCB0aGlzQXJnXSlcbiAgZmlsdGVyOiBmdW5jdGlvbiBmaWx0ZXIoY2FsbGJhY2tmbiAvKiAsIHRoaXNBcmcgKi8pIHtcbiAgICByZXR1cm4gJGZpbHRlcih0aGlzLCBjYWxsYmFja2ZuLCBhcmd1bWVudHNbMV0pO1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIDIyLjEuMy45IEFycmF5LnByb3RvdHlwZS5maW5kSW5kZXgocHJlZGljYXRlLCB0aGlzQXJnID0gdW5kZWZpbmVkKVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciAkZmluZCA9IHJlcXVpcmUoJy4vX2FycmF5LW1ldGhvZHMnKSg2KTtcbnZhciBLRVkgPSAnZmluZEluZGV4JztcbnZhciBmb3JjZWQgPSB0cnVlO1xuLy8gU2hvdWxkbid0IHNraXAgaG9sZXNcbmlmIChLRVkgaW4gW10pIEFycmF5KDEpW0tFWV0oZnVuY3Rpb24gKCkgeyBmb3JjZWQgPSBmYWxzZTsgfSk7XG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqIGZvcmNlZCwgJ0FycmF5Jywge1xuICBmaW5kSW5kZXg6IGZ1bmN0aW9uIGZpbmRJbmRleChjYWxsYmFja2ZuIC8qICwgdGhhdCA9IHVuZGVmaW5lZCAqLykge1xuICAgIHJldHVybiAkZmluZCh0aGlzLCBjYWxsYmFja2ZuLCBhcmd1bWVudHMubGVuZ3RoID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCk7XG4gIH1cbn0pO1xucmVxdWlyZSgnLi9fYWRkLXRvLXVuc2NvcGFibGVzJykoS0VZKTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIDIyLjEuMy44IEFycmF5LnByb3RvdHlwZS5maW5kKHByZWRpY2F0ZSwgdGhpc0FyZyA9IHVuZGVmaW5lZClcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgJGZpbmQgPSByZXF1aXJlKCcuL19hcnJheS1tZXRob2RzJykoNSk7XG52YXIgS0VZID0gJ2ZpbmQnO1xudmFyIGZvcmNlZCA9IHRydWU7XG4vLyBTaG91bGRuJ3Qgc2tpcCBob2xlc1xuaWYgKEtFWSBpbiBbXSkgQXJyYXkoMSlbS0VZXShmdW5jdGlvbiAoKSB7IGZvcmNlZCA9IGZhbHNlOyB9KTtcbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogZm9yY2VkLCAnQXJyYXknLCB7XG4gIGZpbmQ6IGZ1bmN0aW9uIGZpbmQoY2FsbGJhY2tmbiAvKiAsIHRoYXQgPSB1bmRlZmluZWQgKi8pIHtcbiAgICByZXR1cm4gJGZpbmQodGhpcywgY2FsbGJhY2tmbiwgYXJndW1lbnRzLmxlbmd0aCA+IDEgPyBhcmd1bWVudHNbMV0gOiB1bmRlZmluZWQpO1xuICB9XG59KTtcbnJlcXVpcmUoJy4vX2FkZC10by11bnNjb3BhYmxlcycpKEtFWSk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRmb3JFYWNoID0gcmVxdWlyZSgnLi9fYXJyYXktbWV0aG9kcycpKDApO1xudmFyIFNUUklDVCA9IHJlcXVpcmUoJy4vX3N0cmljdC1tZXRob2QnKShbXS5mb3JFYWNoLCB0cnVlKTtcblxuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAhU1RSSUNULCAnQXJyYXknLCB7XG4gIC8vIDIyLjEuMy4xMCAvIDE1LjQuNC4xOCBBcnJheS5wcm90b3R5cGUuZm9yRWFjaChjYWxsYmFja2ZuIFssIHRoaXNBcmddKVxuICBmb3JFYWNoOiBmdW5jdGlvbiBmb3JFYWNoKGNhbGxiYWNrZm4gLyogLCB0aGlzQXJnICovKSB7XG4gICAgcmV0dXJuICRmb3JFYWNoKHRoaXMsIGNhbGxiYWNrZm4sIGFyZ3VtZW50c1sxXSk7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIGN0eCA9IHJlcXVpcmUoJy4vX2N0eCcpO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIGNhbGwgPSByZXF1aXJlKCcuL19pdGVyLWNhbGwnKTtcbnZhciBpc0FycmF5SXRlciA9IHJlcXVpcmUoJy4vX2lzLWFycmF5LWl0ZXInKTtcbnZhciB0b0xlbmd0aCA9IHJlcXVpcmUoJy4vX3RvLWxlbmd0aCcpO1xudmFyIGNyZWF0ZVByb3BlcnR5ID0gcmVxdWlyZSgnLi9fY3JlYXRlLXByb3BlcnR5Jyk7XG52YXIgZ2V0SXRlckZuID0gcmVxdWlyZSgnLi9jb3JlLmdldC1pdGVyYXRvci1tZXRob2QnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiAhcmVxdWlyZSgnLi9faXRlci1kZXRlY3QnKShmdW5jdGlvbiAoaXRlcikgeyBBcnJheS5mcm9tKGl0ZXIpOyB9KSwgJ0FycmF5Jywge1xuICAvLyAyMi4xLjIuMSBBcnJheS5mcm9tKGFycmF5TGlrZSwgbWFwZm4gPSB1bmRlZmluZWQsIHRoaXNBcmcgPSB1bmRlZmluZWQpXG4gIGZyb206IGZ1bmN0aW9uIGZyb20oYXJyYXlMaWtlIC8qICwgbWFwZm4gPSB1bmRlZmluZWQsIHRoaXNBcmcgPSB1bmRlZmluZWQgKi8pIHtcbiAgICB2YXIgTyA9IHRvT2JqZWN0KGFycmF5TGlrZSk7XG4gICAgdmFyIEMgPSB0eXBlb2YgdGhpcyA9PSAnZnVuY3Rpb24nID8gdGhpcyA6IEFycmF5O1xuICAgIHZhciBhTGVuID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgICB2YXIgbWFwZm4gPSBhTGVuID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZDtcbiAgICB2YXIgbWFwcGluZyA9IG1hcGZuICE9PSB1bmRlZmluZWQ7XG4gICAgdmFyIGluZGV4ID0gMDtcbiAgICB2YXIgaXRlckZuID0gZ2V0SXRlckZuKE8pO1xuICAgIHZhciBsZW5ndGgsIHJlc3VsdCwgc3RlcCwgaXRlcmF0b3I7XG4gICAgaWYgKG1hcHBpbmcpIG1hcGZuID0gY3R4KG1hcGZuLCBhTGVuID4gMiA/IGFyZ3VtZW50c1syXSA6IHVuZGVmaW5lZCwgMik7XG4gICAgLy8gaWYgb2JqZWN0IGlzbid0IGl0ZXJhYmxlIG9yIGl0J3MgYXJyYXkgd2l0aCBkZWZhdWx0IGl0ZXJhdG9yIC0gdXNlIHNpbXBsZSBjYXNlXG4gICAgaWYgKGl0ZXJGbiAhPSB1bmRlZmluZWQgJiYgIShDID09IEFycmF5ICYmIGlzQXJyYXlJdGVyKGl0ZXJGbikpKSB7XG4gICAgICBmb3IgKGl0ZXJhdG9yID0gaXRlckZuLmNhbGwoTyksIHJlc3VsdCA9IG5ldyBDKCk7ICEoc3RlcCA9IGl0ZXJhdG9yLm5leHQoKSkuZG9uZTsgaW5kZXgrKykge1xuICAgICAgICBjcmVhdGVQcm9wZXJ0eShyZXN1bHQsIGluZGV4LCBtYXBwaW5nID8gY2FsbChpdGVyYXRvciwgbWFwZm4sIFtzdGVwLnZhbHVlLCBpbmRleF0sIHRydWUpIDogc3RlcC52YWx1ZSk7XG4gICAgICB9XG4gICAgfSBlbHNlIHtcbiAgICAgIGxlbmd0aCA9IHRvTGVuZ3RoKE8ubGVuZ3RoKTtcbiAgICAgIGZvciAocmVzdWx0ID0gbmV3IEMobGVuZ3RoKTsgbGVuZ3RoID4gaW5kZXg7IGluZGV4KyspIHtcbiAgICAgICAgY3JlYXRlUHJvcGVydHkocmVzdWx0LCBpbmRleCwgbWFwcGluZyA/IG1hcGZuKE9baW5kZXhdLCBpbmRleCkgOiBPW2luZGV4XSk7XG4gICAgICB9XG4gICAgfVxuICAgIHJlc3VsdC5sZW5ndGggPSBpbmRleDtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgJGluZGV4T2YgPSByZXF1aXJlKCcuL19hcnJheS1pbmNsdWRlcycpKGZhbHNlKTtcbnZhciAkbmF0aXZlID0gW10uaW5kZXhPZjtcbnZhciBORUdBVElWRV9aRVJPID0gISEkbmF0aXZlICYmIDEgLyBbMV0uaW5kZXhPZigxLCAtMCkgPCAwO1xuXG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqIChORUdBVElWRV9aRVJPIHx8ICFyZXF1aXJlKCcuL19zdHJpY3QtbWV0aG9kJykoJG5hdGl2ZSkpLCAnQXJyYXknLCB7XG4gIC8vIDIyLjEuMy4xMSAvIDE1LjQuNC4xNCBBcnJheS5wcm90b3R5cGUuaW5kZXhPZihzZWFyY2hFbGVtZW50IFssIGZyb21JbmRleF0pXG4gIGluZGV4T2Y6IGZ1bmN0aW9uIGluZGV4T2Yoc2VhcmNoRWxlbWVudCAvKiAsIGZyb21JbmRleCA9IDAgKi8pIHtcbiAgICByZXR1cm4gTkVHQVRJVkVfWkVST1xuICAgICAgLy8gY29udmVydCAtMCB0byArMFxuICAgICAgPyAkbmF0aXZlLmFwcGx5KHRoaXMsIGFyZ3VtZW50cykgfHwgMFxuICAgICAgOiAkaW5kZXhPZih0aGlzLCBzZWFyY2hFbGVtZW50LCBhcmd1bWVudHNbMV0pO1xuICB9XG59KTtcbiIsIi8vIDIyLjEuMi4yIC8gMTUuNC4zLjIgQXJyYXkuaXNBcnJheShhcmcpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ0FycmF5JywgeyBpc0FycmF5OiByZXF1aXJlKCcuL19pcy1hcnJheScpIH0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIGFkZFRvVW5zY29wYWJsZXMgPSByZXF1aXJlKCcuL19hZGQtdG8tdW5zY29wYWJsZXMnKTtcbnZhciBzdGVwID0gcmVxdWlyZSgnLi9faXRlci1zdGVwJyk7XG52YXIgSXRlcmF0b3JzID0gcmVxdWlyZSgnLi9faXRlcmF0b3JzJyk7XG52YXIgdG9JT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8taW9iamVjdCcpO1xuXG4vLyAyMi4xLjMuNCBBcnJheS5wcm90b3R5cGUuZW50cmllcygpXG4vLyAyMi4xLjMuMTMgQXJyYXkucHJvdG90eXBlLmtleXMoKVxuLy8gMjIuMS4zLjI5IEFycmF5LnByb3RvdHlwZS52YWx1ZXMoKVxuLy8gMjIuMS4zLjMwIEFycmF5LnByb3RvdHlwZVtAQGl0ZXJhdG9yXSgpXG5tb2R1bGUuZXhwb3J0cyA9IHJlcXVpcmUoJy4vX2l0ZXItZGVmaW5lJykoQXJyYXksICdBcnJheScsIGZ1bmN0aW9uIChpdGVyYXRlZCwga2luZCkge1xuICB0aGlzLl90ID0gdG9JT2JqZWN0KGl0ZXJhdGVkKTsgLy8gdGFyZ2V0XG4gIHRoaXMuX2kgPSAwOyAgICAgICAgICAgICAgICAgICAvLyBuZXh0IGluZGV4XG4gIHRoaXMuX2sgPSBraW5kOyAgICAgICAgICAgICAgICAvLyBraW5kXG4vLyAyMi4xLjUuMi4xICVBcnJheUl0ZXJhdG9yUHJvdG90eXBlJS5uZXh0KClcbn0sIGZ1bmN0aW9uICgpIHtcbiAgdmFyIE8gPSB0aGlzLl90O1xuICB2YXIga2luZCA9IHRoaXMuX2s7XG4gIHZhciBpbmRleCA9IHRoaXMuX2krKztcbiAgaWYgKCFPIHx8IGluZGV4ID49IE8ubGVuZ3RoKSB7XG4gICAgdGhpcy5fdCA9IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gc3RlcCgxKTtcbiAgfVxuICBpZiAoa2luZCA9PSAna2V5cycpIHJldHVybiBzdGVwKDAsIGluZGV4KTtcbiAgaWYgKGtpbmQgPT0gJ3ZhbHVlcycpIHJldHVybiBzdGVwKDAsIE9baW5kZXhdKTtcbiAgcmV0dXJuIHN0ZXAoMCwgW2luZGV4LCBPW2luZGV4XV0pO1xufSwgJ3ZhbHVlcycpO1xuXG4vLyBhcmd1bWVudHNMaXN0W0BAaXRlcmF0b3JdIGlzICVBcnJheVByb3RvX3ZhbHVlcyUgKDkuNC40LjYsIDkuNC40LjcpXG5JdGVyYXRvcnMuQXJndW1lbnRzID0gSXRlcmF0b3JzLkFycmF5O1xuXG5hZGRUb1Vuc2NvcGFibGVzKCdrZXlzJyk7XG5hZGRUb1Vuc2NvcGFibGVzKCd2YWx1ZXMnKTtcbmFkZFRvVW5zY29wYWJsZXMoJ2VudHJpZXMnKTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIDIyLjEuMy4xMyBBcnJheS5wcm90b3R5cGUuam9pbihzZXBhcmF0b3IpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIHRvSU9iamVjdCA9IHJlcXVpcmUoJy4vX3RvLWlvYmplY3QnKTtcbnZhciBhcnJheUpvaW4gPSBbXS5qb2luO1xuXG4vLyBmYWxsYmFjayBmb3Igbm90IGFycmF5LWxpa2Ugc3RyaW5nc1xuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAocmVxdWlyZSgnLi9faW9iamVjdCcpICE9IE9iamVjdCB8fCAhcmVxdWlyZSgnLi9fc3RyaWN0LW1ldGhvZCcpKGFycmF5Sm9pbikpLCAnQXJyYXknLCB7XG4gIGpvaW46IGZ1bmN0aW9uIGpvaW4oc2VwYXJhdG9yKSB7XG4gICAgcmV0dXJuIGFycmF5Sm9pbi5jYWxsKHRvSU9iamVjdCh0aGlzKSwgc2VwYXJhdG9yID09PSB1bmRlZmluZWQgPyAnLCcgOiBzZXBhcmF0b3IpO1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgdG9JT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8taW9iamVjdCcpO1xudmFyIHRvSW50ZWdlciA9IHJlcXVpcmUoJy4vX3RvLWludGVnZXInKTtcbnZhciB0b0xlbmd0aCA9IHJlcXVpcmUoJy4vX3RvLWxlbmd0aCcpO1xudmFyICRuYXRpdmUgPSBbXS5sYXN0SW5kZXhPZjtcbnZhciBORUdBVElWRV9aRVJPID0gISEkbmF0aXZlICYmIDEgLyBbMV0ubGFzdEluZGV4T2YoMSwgLTApIDwgMDtcblxuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAoTkVHQVRJVkVfWkVSTyB8fCAhcmVxdWlyZSgnLi9fc3RyaWN0LW1ldGhvZCcpKCRuYXRpdmUpKSwgJ0FycmF5Jywge1xuICAvLyAyMi4xLjMuMTQgLyAxNS40LjQuMTUgQXJyYXkucHJvdG90eXBlLmxhc3RJbmRleE9mKHNlYXJjaEVsZW1lbnQgWywgZnJvbUluZGV4XSlcbiAgbGFzdEluZGV4T2Y6IGZ1bmN0aW9uIGxhc3RJbmRleE9mKHNlYXJjaEVsZW1lbnQgLyogLCBmcm9tSW5kZXggPSBAWyotMV0gKi8pIHtcbiAgICAvLyBjb252ZXJ0IC0wIHRvICswXG4gICAgaWYgKE5FR0FUSVZFX1pFUk8pIHJldHVybiAkbmF0aXZlLmFwcGx5KHRoaXMsIGFyZ3VtZW50cykgfHwgMDtcbiAgICB2YXIgTyA9IHRvSU9iamVjdCh0aGlzKTtcbiAgICB2YXIgbGVuZ3RoID0gdG9MZW5ndGgoTy5sZW5ndGgpO1xuICAgIHZhciBpbmRleCA9IGxlbmd0aCAtIDE7XG4gICAgaWYgKGFyZ3VtZW50cy5sZW5ndGggPiAxKSBpbmRleCA9IE1hdGgubWluKGluZGV4LCB0b0ludGVnZXIoYXJndW1lbnRzWzFdKSk7XG4gICAgaWYgKGluZGV4IDwgMCkgaW5kZXggPSBsZW5ndGggKyBpbmRleDtcbiAgICBmb3IgKDtpbmRleCA+PSAwOyBpbmRleC0tKSBpZiAoaW5kZXggaW4gTykgaWYgKE9baW5kZXhdID09PSBzZWFyY2hFbGVtZW50KSByZXR1cm4gaW5kZXggfHwgMDtcbiAgICByZXR1cm4gLTE7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciAkbWFwID0gcmVxdWlyZSgnLi9fYXJyYXktbWV0aG9kcycpKDEpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqICFyZXF1aXJlKCcuL19zdHJpY3QtbWV0aG9kJykoW10ubWFwLCB0cnVlKSwgJ0FycmF5Jywge1xuICAvLyAyMi4xLjMuMTUgLyAxNS40LjQuMTkgQXJyYXkucHJvdG90eXBlLm1hcChjYWxsYmFja2ZuIFssIHRoaXNBcmddKVxuICBtYXA6IGZ1bmN0aW9uIG1hcChjYWxsYmFja2ZuIC8qICwgdGhpc0FyZyAqLykge1xuICAgIHJldHVybiAkbWFwKHRoaXMsIGNhbGxiYWNrZm4sIGFyZ3VtZW50c1sxXSk7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBjcmVhdGVQcm9wZXJ0eSA9IHJlcXVpcmUoJy4vX2NyZWF0ZS1wcm9wZXJ0eScpO1xuXG4vLyBXZWJLaXQgQXJyYXkub2YgaXNuJ3QgZ2VuZXJpY1xuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiByZXF1aXJlKCcuL19mYWlscycpKGZ1bmN0aW9uICgpIHtcbiAgZnVuY3Rpb24gRigpIHsgLyogZW1wdHkgKi8gfVxuICByZXR1cm4gIShBcnJheS5vZi5jYWxsKEYpIGluc3RhbmNlb2YgRik7XG59KSwgJ0FycmF5Jywge1xuICAvLyAyMi4xLjIuMyBBcnJheS5vZiggLi4uaXRlbXMpXG4gIG9mOiBmdW5jdGlvbiBvZigvKiAuLi5hcmdzICovKSB7XG4gICAgdmFyIGluZGV4ID0gMDtcbiAgICB2YXIgYUxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gICAgdmFyIHJlc3VsdCA9IG5ldyAodHlwZW9mIHRoaXMgPT0gJ2Z1bmN0aW9uJyA/IHRoaXMgOiBBcnJheSkoYUxlbik7XG4gICAgd2hpbGUgKGFMZW4gPiBpbmRleCkgY3JlYXRlUHJvcGVydHkocmVzdWx0LCBpbmRleCwgYXJndW1lbnRzW2luZGV4KytdKTtcbiAgICByZXN1bHQubGVuZ3RoID0gYUxlbjtcbiAgICByZXR1cm4gcmVzdWx0O1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgJHJlZHVjZSA9IHJlcXVpcmUoJy4vX2FycmF5LXJlZHVjZScpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqICFyZXF1aXJlKCcuL19zdHJpY3QtbWV0aG9kJykoW10ucmVkdWNlUmlnaHQsIHRydWUpLCAnQXJyYXknLCB7XG4gIC8vIDIyLjEuMy4xOSAvIDE1LjQuNC4yMiBBcnJheS5wcm90b3R5cGUucmVkdWNlUmlnaHQoY2FsbGJhY2tmbiBbLCBpbml0aWFsVmFsdWVdKVxuICByZWR1Y2VSaWdodDogZnVuY3Rpb24gcmVkdWNlUmlnaHQoY2FsbGJhY2tmbiAvKiAsIGluaXRpYWxWYWx1ZSAqLykge1xuICAgIHJldHVybiAkcmVkdWNlKHRoaXMsIGNhbGxiYWNrZm4sIGFyZ3VtZW50cy5sZW5ndGgsIGFyZ3VtZW50c1sxXSwgdHJ1ZSk7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciAkcmVkdWNlID0gcmVxdWlyZSgnLi9fYXJyYXktcmVkdWNlJyk7XG5cbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogIXJlcXVpcmUoJy4vX3N0cmljdC1tZXRob2QnKShbXS5yZWR1Y2UsIHRydWUpLCAnQXJyYXknLCB7XG4gIC8vIDIyLjEuMy4xOCAvIDE1LjQuNC4yMSBBcnJheS5wcm90b3R5cGUucmVkdWNlKGNhbGxiYWNrZm4gWywgaW5pdGlhbFZhbHVlXSlcbiAgcmVkdWNlOiBmdW5jdGlvbiByZWR1Y2UoY2FsbGJhY2tmbiAvKiAsIGluaXRpYWxWYWx1ZSAqLykge1xuICAgIHJldHVybiAkcmVkdWNlKHRoaXMsIGNhbGxiYWNrZm4sIGFyZ3VtZW50cy5sZW5ndGgsIGFyZ3VtZW50c1sxXSwgZmFsc2UpO1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgaHRtbCA9IHJlcXVpcmUoJy4vX2h0bWwnKTtcbnZhciBjb2YgPSByZXF1aXJlKCcuL19jb2YnKTtcbnZhciB0b0Fic29sdXRlSW5kZXggPSByZXF1aXJlKCcuL190by1hYnNvbHV0ZS1pbmRleCcpO1xudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG52YXIgYXJyYXlTbGljZSA9IFtdLnNsaWNlO1xuXG4vLyBmYWxsYmFjayBmb3Igbm90IGFycmF5LWxpa2UgRVMzIHN0cmluZ3MgYW5kIERPTSBvYmplY3RzXG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqIHJlcXVpcmUoJy4vX2ZhaWxzJykoZnVuY3Rpb24gKCkge1xuICBpZiAoaHRtbCkgYXJyYXlTbGljZS5jYWxsKGh0bWwpO1xufSksICdBcnJheScsIHtcbiAgc2xpY2U6IGZ1bmN0aW9uIHNsaWNlKGJlZ2luLCBlbmQpIHtcbiAgICB2YXIgbGVuID0gdG9MZW5ndGgodGhpcy5sZW5ndGgpO1xuICAgIHZhciBrbGFzcyA9IGNvZih0aGlzKTtcbiAgICBlbmQgPSBlbmQgPT09IHVuZGVmaW5lZCA/IGxlbiA6IGVuZDtcbiAgICBpZiAoa2xhc3MgPT0gJ0FycmF5JykgcmV0dXJuIGFycmF5U2xpY2UuY2FsbCh0aGlzLCBiZWdpbiwgZW5kKTtcbiAgICB2YXIgc3RhcnQgPSB0b0Fic29sdXRlSW5kZXgoYmVnaW4sIGxlbik7XG4gICAgdmFyIHVwVG8gPSB0b0Fic29sdXRlSW5kZXgoZW5kLCBsZW4pO1xuICAgIHZhciBzaXplID0gdG9MZW5ndGgodXBUbyAtIHN0YXJ0KTtcbiAgICB2YXIgY2xvbmVkID0gbmV3IEFycmF5KHNpemUpO1xuICAgIHZhciBpID0gMDtcbiAgICBmb3IgKDsgaSA8IHNpemU7IGkrKykgY2xvbmVkW2ldID0ga2xhc3MgPT0gJ1N0cmluZydcbiAgICAgID8gdGhpcy5jaGFyQXQoc3RhcnQgKyBpKVxuICAgICAgOiB0aGlzW3N0YXJ0ICsgaV07XG4gICAgcmV0dXJuIGNsb25lZDtcbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRzb21lID0gcmVxdWlyZSgnLi9fYXJyYXktbWV0aG9kcycpKDMpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqICFyZXF1aXJlKCcuL19zdHJpY3QtbWV0aG9kJykoW10uc29tZSwgdHJ1ZSksICdBcnJheScsIHtcbiAgLy8gMjIuMS4zLjIzIC8gMTUuNC40LjE3IEFycmF5LnByb3RvdHlwZS5zb21lKGNhbGxiYWNrZm4gWywgdGhpc0FyZ10pXG4gIHNvbWU6IGZ1bmN0aW9uIHNvbWUoY2FsbGJhY2tmbiAvKiAsIHRoaXNBcmcgKi8pIHtcbiAgICByZXR1cm4gJHNvbWUodGhpcywgY2FsbGJhY2tmbiwgYXJndW1lbnRzWzFdKTtcbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGFGdW5jdGlvbiA9IHJlcXVpcmUoJy4vX2EtZnVuY3Rpb24nKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIGZhaWxzID0gcmVxdWlyZSgnLi9fZmFpbHMnKTtcbnZhciAkc29ydCA9IFtdLnNvcnQ7XG52YXIgdGVzdCA9IFsxLCAyLCAzXTtcblxuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAoZmFpbHMoZnVuY3Rpb24gKCkge1xuICAvLyBJRTgtXG4gIHRlc3Quc29ydCh1bmRlZmluZWQpO1xufSkgfHwgIWZhaWxzKGZ1bmN0aW9uICgpIHtcbiAgLy8gVjggYnVnXG4gIHRlc3Quc29ydChudWxsKTtcbiAgLy8gT2xkIFdlYktpdFxufSkgfHwgIXJlcXVpcmUoJy4vX3N0cmljdC1tZXRob2QnKSgkc29ydCkpLCAnQXJyYXknLCB7XG4gIC8vIDIyLjEuMy4yNSBBcnJheS5wcm90b3R5cGUuc29ydChjb21wYXJlZm4pXG4gIHNvcnQ6IGZ1bmN0aW9uIHNvcnQoY29tcGFyZWZuKSB7XG4gICAgcmV0dXJuIGNvbXBhcmVmbiA9PT0gdW5kZWZpbmVkXG4gICAgICA/ICRzb3J0LmNhbGwodG9PYmplY3QodGhpcykpXG4gICAgICA6ICRzb3J0LmNhbGwodG9PYmplY3QodGhpcyksIGFGdW5jdGlvbihjb21wYXJlZm4pKTtcbiAgfVxufSk7XG4iLCJyZXF1aXJlKCcuL19zZXQtc3BlY2llcycpKCdBcnJheScpO1xuIiwiLy8gMjAuMy4zLjEgLyAxNS45LjQuNCBEYXRlLm5vdygpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ0RhdGUnLCB7IG5vdzogZnVuY3Rpb24gKCkgeyByZXR1cm4gbmV3IERhdGUoKS5nZXRUaW1lKCk7IH0gfSk7XG4iLCIvLyAyMC4zLjQuMzYgLyAxNS45LjUuNDMgRGF0ZS5wcm90b3R5cGUudG9JU09TdHJpbmcoKVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciB0b0lTT1N0cmluZyA9IHJlcXVpcmUoJy4vX2RhdGUtdG8taXNvLXN0cmluZycpO1xuXG4vLyBQaGFudG9tSlMgLyBvbGQgV2ViS2l0IGhhcyBhIGJyb2tlbiBpbXBsZW1lbnRhdGlvbnNcbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogKERhdGUucHJvdG90eXBlLnRvSVNPU3RyaW5nICE9PSB0b0lTT1N0cmluZyksICdEYXRlJywge1xuICB0b0lTT1N0cmluZzogdG9JU09TdHJpbmdcbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIHRvUHJpbWl0aXZlID0gcmVxdWlyZSgnLi9fdG8tcHJpbWl0aXZlJyk7XG5cbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogcmVxdWlyZSgnLi9fZmFpbHMnKShmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBuZXcgRGF0ZShOYU4pLnRvSlNPTigpICE9PSBudWxsXG4gICAgfHwgRGF0ZS5wcm90b3R5cGUudG9KU09OLmNhbGwoeyB0b0lTT1N0cmluZzogZnVuY3Rpb24gKCkgeyByZXR1cm4gMTsgfSB9KSAhPT0gMTtcbn0pLCAnRGF0ZScsIHtcbiAgLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIG5vLXVudXNlZC12YXJzXG4gIHRvSlNPTjogZnVuY3Rpb24gdG9KU09OKGtleSkge1xuICAgIHZhciBPID0gdG9PYmplY3QodGhpcyk7XG4gICAgdmFyIHB2ID0gdG9QcmltaXRpdmUoTyk7XG4gICAgcmV0dXJuIHR5cGVvZiBwdiA9PSAnbnVtYmVyJyAmJiAhaXNGaW5pdGUocHYpID8gbnVsbCA6IE8udG9JU09TdHJpbmcoKTtcbiAgfVxufSk7XG4iLCJ2YXIgVE9fUFJJTUlUSVZFID0gcmVxdWlyZSgnLi9fd2tzJykoJ3RvUHJpbWl0aXZlJyk7XG52YXIgcHJvdG8gPSBEYXRlLnByb3RvdHlwZTtcblxuaWYgKCEoVE9fUFJJTUlUSVZFIGluIHByb3RvKSkgcmVxdWlyZSgnLi9faGlkZScpKHByb3RvLCBUT19QUklNSVRJVkUsIHJlcXVpcmUoJy4vX2RhdGUtdG8tcHJpbWl0aXZlJykpO1xuIiwidmFyIERhdGVQcm90byA9IERhdGUucHJvdG90eXBlO1xudmFyIElOVkFMSURfREFURSA9ICdJbnZhbGlkIERhdGUnO1xudmFyIFRPX1NUUklORyA9ICd0b1N0cmluZyc7XG52YXIgJHRvU3RyaW5nID0gRGF0ZVByb3RvW1RPX1NUUklOR107XG52YXIgZ2V0VGltZSA9IERhdGVQcm90by5nZXRUaW1lO1xuaWYgKG5ldyBEYXRlKE5hTikgKyAnJyAhPSBJTlZBTElEX0RBVEUpIHtcbiAgcmVxdWlyZSgnLi9fcmVkZWZpbmUnKShEYXRlUHJvdG8sIFRPX1NUUklORywgZnVuY3Rpb24gdG9TdHJpbmcoKSB7XG4gICAgdmFyIHZhbHVlID0gZ2V0VGltZS5jYWxsKHRoaXMpO1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1zZWxmLWNvbXBhcmVcbiAgICByZXR1cm4gdmFsdWUgPT09IHZhbHVlID8gJHRvU3RyaW5nLmNhbGwodGhpcykgOiBJTlZBTElEX0RBVEU7XG4gIH0pO1xufVxuIiwiLy8gMTkuMi4zLjIgLyAxNS4zLjQuNSBGdW5jdGlvbi5wcm90b3R5cGUuYmluZCh0aGlzQXJnLCBhcmdzLi4uKVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlAsICdGdW5jdGlvbicsIHsgYmluZDogcmVxdWlyZSgnLi9fYmluZCcpIH0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG52YXIgZ2V0UHJvdG90eXBlT2YgPSByZXF1aXJlKCcuL19vYmplY3QtZ3BvJyk7XG52YXIgSEFTX0lOU1RBTkNFID0gcmVxdWlyZSgnLi9fd2tzJykoJ2hhc0luc3RhbmNlJyk7XG52YXIgRnVuY3Rpb25Qcm90byA9IEZ1bmN0aW9uLnByb3RvdHlwZTtcbi8vIDE5LjIuMy42IEZ1bmN0aW9uLnByb3RvdHlwZVtAQGhhc0luc3RhbmNlXShWKVxuaWYgKCEoSEFTX0lOU1RBTkNFIGluIEZ1bmN0aW9uUHJvdG8pKSByZXF1aXJlKCcuL19vYmplY3QtZHAnKS5mKEZ1bmN0aW9uUHJvdG8sIEhBU19JTlNUQU5DRSwgeyB2YWx1ZTogZnVuY3Rpb24gKE8pIHtcbiAgaWYgKHR5cGVvZiB0aGlzICE9ICdmdW5jdGlvbicgfHwgIWlzT2JqZWN0KE8pKSByZXR1cm4gZmFsc2U7XG4gIGlmICghaXNPYmplY3QodGhpcy5wcm90b3R5cGUpKSByZXR1cm4gTyBpbnN0YW5jZW9mIHRoaXM7XG4gIC8vIGZvciBlbnZpcm9ubWVudCB3L28gbmF0aXZlIGBAQGhhc0luc3RhbmNlYCBsb2dpYyBlbm91Z2ggYGluc3RhbmNlb2ZgLCBidXQgYWRkIHRoaXM6XG4gIHdoaWxlIChPID0gZ2V0UHJvdG90eXBlT2YoTykpIGlmICh0aGlzLnByb3RvdHlwZSA9PT0gTykgcmV0dXJuIHRydWU7XG4gIHJldHVybiBmYWxzZTtcbn0gfSk7XG4iLCJ2YXIgZFAgPSByZXF1aXJlKCcuL19vYmplY3QtZHAnKS5mO1xudmFyIEZQcm90byA9IEZ1bmN0aW9uLnByb3RvdHlwZTtcbnZhciBuYW1lUkUgPSAvXlxccypmdW5jdGlvbiAoW14gKF0qKS87XG52YXIgTkFNRSA9ICduYW1lJztcblxuLy8gMTkuMi40LjIgbmFtZVxuTkFNRSBpbiBGUHJvdG8gfHwgcmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKSAmJiBkUChGUHJvdG8sIE5BTUUsIHtcbiAgY29uZmlndXJhYmxlOiB0cnVlLFxuICBnZXQ6IGZ1bmN0aW9uICgpIHtcbiAgICB0cnkge1xuICAgICAgcmV0dXJuICgnJyArIHRoaXMpLm1hdGNoKG5hbWVSRSlbMV07XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgcmV0dXJuICcnO1xuICAgIH1cbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgc3Ryb25nID0gcmVxdWlyZSgnLi9fY29sbGVjdGlvbi1zdHJvbmcnKTtcbnZhciB2YWxpZGF0ZSA9IHJlcXVpcmUoJy4vX3ZhbGlkYXRlLWNvbGxlY3Rpb24nKTtcbnZhciBNQVAgPSAnTWFwJztcblxuLy8gMjMuMSBNYXAgT2JqZWN0c1xubW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKCcuL19jb2xsZWN0aW9uJykoTUFQLCBmdW5jdGlvbiAoZ2V0KSB7XG4gIHJldHVybiBmdW5jdGlvbiBNYXAoKSB7IHJldHVybiBnZXQodGhpcywgYXJndW1lbnRzLmxlbmd0aCA+IDAgPyBhcmd1bWVudHNbMF0gOiB1bmRlZmluZWQpOyB9O1xufSwge1xuICAvLyAyMy4xLjMuNiBNYXAucHJvdG90eXBlLmdldChrZXkpXG4gIGdldDogZnVuY3Rpb24gZ2V0KGtleSkge1xuICAgIHZhciBlbnRyeSA9IHN0cm9uZy5nZXRFbnRyeSh2YWxpZGF0ZSh0aGlzLCBNQVApLCBrZXkpO1xuICAgIHJldHVybiBlbnRyeSAmJiBlbnRyeS52O1xuICB9LFxuICAvLyAyMy4xLjMuOSBNYXAucHJvdG90eXBlLnNldChrZXksIHZhbHVlKVxuICBzZXQ6IGZ1bmN0aW9uIHNldChrZXksIHZhbHVlKSB7XG4gICAgcmV0dXJuIHN0cm9uZy5kZWYodmFsaWRhdGUodGhpcywgTUFQKSwga2V5ID09PSAwID8gMCA6IGtleSwgdmFsdWUpO1xuICB9XG59LCBzdHJvbmcsIHRydWUpO1xuIiwiLy8gMjAuMi4yLjMgTWF0aC5hY29zaCh4KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBsb2cxcCA9IHJlcXVpcmUoJy4vX21hdGgtbG9nMXAnKTtcbnZhciBzcXJ0ID0gTWF0aC5zcXJ0O1xudmFyICRhY29zaCA9IE1hdGguYWNvc2g7XG5cbiRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogISgkYWNvc2hcbiAgLy8gVjggYnVnOiBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3Y4L2lzc3Vlcy9kZXRhaWw/aWQ9MzUwOVxuICAmJiBNYXRoLmZsb29yKCRhY29zaChOdW1iZXIuTUFYX1ZBTFVFKSkgPT0gNzEwXG4gIC8vIFRvciBCcm93c2VyIGJ1ZzogTWF0aC5hY29zaChJbmZpbml0eSkgLT4gTmFOXG4gICYmICRhY29zaChJbmZpbml0eSkgPT0gSW5maW5pdHlcbiksICdNYXRoJywge1xuICBhY29zaDogZnVuY3Rpb24gYWNvc2goeCkge1xuICAgIHJldHVybiAoeCA9ICt4KSA8IDEgPyBOYU4gOiB4ID4gOTQ5MDYyNjUuNjI0MjUxNTZcbiAgICAgID8gTWF0aC5sb2coeCkgKyBNYXRoLkxOMlxuICAgICAgOiBsb2cxcCh4IC0gMSArIHNxcnQoeCAtIDEpICogc3FydCh4ICsgMSkpO1xuICB9XG59KTtcbiIsIi8vIDIwLjIuMi41IE1hdGguYXNpbmgoeClcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgJGFzaW5oID0gTWF0aC5hc2luaDtcblxuZnVuY3Rpb24gYXNpbmgoeCkge1xuICByZXR1cm4gIWlzRmluaXRlKHggPSAreCkgfHwgeCA9PSAwID8geCA6IHggPCAwID8gLWFzaW5oKC14KSA6IE1hdGgubG9nKHggKyBNYXRoLnNxcnQoeCAqIHggKyAxKSk7XG59XG5cbi8vIFRvciBCcm93c2VyIGJ1ZzogTWF0aC5hc2luaCgwKSAtPiAtMFxuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiAhKCRhc2luaCAmJiAxIC8gJGFzaW5oKDApID4gMCksICdNYXRoJywgeyBhc2luaDogYXNpbmggfSk7XG4iLCIvLyAyMC4yLjIuNyBNYXRoLmF0YW5oKHgpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRhdGFuaCA9IE1hdGguYXRhbmg7XG5cbi8vIFRvciBCcm93c2VyIGJ1ZzogTWF0aC5hdGFuaCgtMCkgLT4gMFxuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiAhKCRhdGFuaCAmJiAxIC8gJGF0YW5oKC0wKSA8IDApLCAnTWF0aCcsIHtcbiAgYXRhbmg6IGZ1bmN0aW9uIGF0YW5oKHgpIHtcbiAgICByZXR1cm4gKHggPSAreCkgPT0gMCA/IHggOiBNYXRoLmxvZygoMSArIHgpIC8gKDEgLSB4KSkgLyAyO1xuICB9XG59KTtcbiIsIi8vIDIwLjIuMi45IE1hdGguY2JydCh4KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBzaWduID0gcmVxdWlyZSgnLi9fbWF0aC1zaWduJyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTWF0aCcsIHtcbiAgY2JydDogZnVuY3Rpb24gY2JydCh4KSB7XG4gICAgcmV0dXJuIHNpZ24oeCA9ICt4KSAqIE1hdGgucG93KE1hdGguYWJzKHgpLCAxIC8gMyk7XG4gIH1cbn0pO1xuIiwiLy8gMjAuMi4yLjExIE1hdGguY2x6MzIoeClcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTWF0aCcsIHtcbiAgY2x6MzI6IGZ1bmN0aW9uIGNsejMyKHgpIHtcbiAgICByZXR1cm4gKHggPj4+PSAwKSA/IDMxIC0gTWF0aC5mbG9vcihNYXRoLmxvZyh4ICsgMC41KSAqIE1hdGguTE9HMkUpIDogMzI7XG4gIH1cbn0pO1xuIiwiLy8gMjAuMi4yLjEyIE1hdGguY29zaCh4KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBleHAgPSBNYXRoLmV4cDtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywge1xuICBjb3NoOiBmdW5jdGlvbiBjb3NoKHgpIHtcbiAgICByZXR1cm4gKGV4cCh4ID0gK3gpICsgZXhwKC14KSkgLyAyO1xuICB9XG59KTtcbiIsIi8vIDIwLjIuMi4xNCBNYXRoLmV4cG0xKHgpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRleHBtMSA9IHJlcXVpcmUoJy4vX21hdGgtZXhwbTEnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiAoJGV4cG0xICE9IE1hdGguZXhwbTEpLCAnTWF0aCcsIHsgZXhwbTE6ICRleHBtMSB9KTtcbiIsIi8vIDIwLjIuMi4xNiBNYXRoLmZyb3VuZCh4KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywgeyBmcm91bmQ6IHJlcXVpcmUoJy4vX21hdGgtZnJvdW5kJykgfSk7XG4iLCIvLyAyMC4yLjIuMTcgTWF0aC5oeXBvdChbdmFsdWUxWywgdmFsdWUyWywg4oCmIF1dXSlcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgYWJzID0gTWF0aC5hYnM7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTWF0aCcsIHtcbiAgaHlwb3Q6IGZ1bmN0aW9uIGh5cG90KHZhbHVlMSwgdmFsdWUyKSB7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW51c2VkLXZhcnNcbiAgICB2YXIgc3VtID0gMDtcbiAgICB2YXIgaSA9IDA7XG4gICAgdmFyIGFMZW4gPSBhcmd1bWVudHMubGVuZ3RoO1xuICAgIHZhciBsYXJnID0gMDtcbiAgICB2YXIgYXJnLCBkaXY7XG4gICAgd2hpbGUgKGkgPCBhTGVuKSB7XG4gICAgICBhcmcgPSBhYnMoYXJndW1lbnRzW2krK10pO1xuICAgICAgaWYgKGxhcmcgPCBhcmcpIHtcbiAgICAgICAgZGl2ID0gbGFyZyAvIGFyZztcbiAgICAgICAgc3VtID0gc3VtICogZGl2ICogZGl2ICsgMTtcbiAgICAgICAgbGFyZyA9IGFyZztcbiAgICAgIH0gZWxzZSBpZiAoYXJnID4gMCkge1xuICAgICAgICBkaXYgPSBhcmcgLyBsYXJnO1xuICAgICAgICBzdW0gKz0gZGl2ICogZGl2O1xuICAgICAgfSBlbHNlIHN1bSArPSBhcmc7XG4gICAgfVxuICAgIHJldHVybiBsYXJnID09PSBJbmZpbml0eSA/IEluZmluaXR5IDogbGFyZyAqIE1hdGguc3FydChzdW0pO1xuICB9XG59KTtcbiIsIi8vIDIwLjIuMi4xOCBNYXRoLmltdWwoeCwgeSlcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgJGltdWwgPSBNYXRoLmltdWw7XG5cbi8vIHNvbWUgV2ViS2l0IHZlcnNpb25zIGZhaWxzIHdpdGggYmlnIG51bWJlcnMsIHNvbWUgaGFzIHdyb25nIGFyaXR5XG4kZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqIHJlcXVpcmUoJy4vX2ZhaWxzJykoZnVuY3Rpb24gKCkge1xuICByZXR1cm4gJGltdWwoMHhmZmZmZmZmZiwgNSkgIT0gLTUgfHwgJGltdWwubGVuZ3RoICE9IDI7XG59KSwgJ01hdGgnLCB7XG4gIGltdWw6IGZ1bmN0aW9uIGltdWwoeCwgeSkge1xuICAgIHZhciBVSU5UMTYgPSAweGZmZmY7XG4gICAgdmFyIHhuID0gK3g7XG4gICAgdmFyIHluID0gK3k7XG4gICAgdmFyIHhsID0gVUlOVDE2ICYgeG47XG4gICAgdmFyIHlsID0gVUlOVDE2ICYgeW47XG4gICAgcmV0dXJuIDAgfCB4bCAqIHlsICsgKChVSU5UMTYgJiB4biA+Pj4gMTYpICogeWwgKyB4bCAqIChVSU5UMTYgJiB5biA+Pj4gMTYpIDw8IDE2ID4+PiAwKTtcbiAgfVxufSk7XG4iLCIvLyAyMC4yLjIuMjEgTWF0aC5sb2cxMCh4KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywge1xuICBsb2cxMDogZnVuY3Rpb24gbG9nMTAoeCkge1xuICAgIHJldHVybiBNYXRoLmxvZyh4KSAqIE1hdGguTE9HMTBFO1xuICB9XG59KTtcbiIsIi8vIDIwLjIuMi4yMCBNYXRoLmxvZzFwKHgpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ01hdGgnLCB7IGxvZzFwOiByZXF1aXJlKCcuL19tYXRoLWxvZzFwJykgfSk7XG4iLCIvLyAyMC4yLjIuMjIgTWF0aC5sb2cyKHgpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ01hdGgnLCB7XG4gIGxvZzI6IGZ1bmN0aW9uIGxvZzIoeCkge1xuICAgIHJldHVybiBNYXRoLmxvZyh4KSAvIE1hdGguTE4yO1xuICB9XG59KTtcbiIsIi8vIDIwLjIuMi4yOCBNYXRoLnNpZ24oeClcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTWF0aCcsIHsgc2lnbjogcmVxdWlyZSgnLi9fbWF0aC1zaWduJykgfSk7XG4iLCIvLyAyMC4yLjIuMzAgTWF0aC5zaW5oKHgpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGV4cG0xID0gcmVxdWlyZSgnLi9fbWF0aC1leHBtMScpO1xudmFyIGV4cCA9IE1hdGguZXhwO1xuXG4vLyBWOCBuZWFyIENocm9taXVtIDM4IGhhcyBhIHByb2JsZW0gd2l0aCB2ZXJ5IHNtYWxsIG51bWJlcnNcbiRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogcmVxdWlyZSgnLi9fZmFpbHMnKShmdW5jdGlvbiAoKSB7XG4gIHJldHVybiAhTWF0aC5zaW5oKC0yZS0xNykgIT0gLTJlLTE3O1xufSksICdNYXRoJywge1xuICBzaW5oOiBmdW5jdGlvbiBzaW5oKHgpIHtcbiAgICByZXR1cm4gTWF0aC5hYnMoeCA9ICt4KSA8IDFcbiAgICAgID8gKGV4cG0xKHgpIC0gZXhwbTEoLXgpKSAvIDJcbiAgICAgIDogKGV4cCh4IC0gMSkgLSBleHAoLXggLSAxKSkgKiAoTWF0aC5FIC8gMik7XG4gIH1cbn0pO1xuIiwiLy8gMjAuMi4yLjMzIE1hdGgudGFuaCh4KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBleHBtMSA9IHJlcXVpcmUoJy4vX21hdGgtZXhwbTEnKTtcbnZhciBleHAgPSBNYXRoLmV4cDtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywge1xuICB0YW5oOiBmdW5jdGlvbiB0YW5oKHgpIHtcbiAgICB2YXIgYSA9IGV4cG0xKHggPSAreCk7XG4gICAgdmFyIGIgPSBleHBtMSgteCk7XG4gICAgcmV0dXJuIGEgPT0gSW5maW5pdHkgPyAxIDogYiA9PSBJbmZpbml0eSA/IC0xIDogKGEgLSBiKSAvIChleHAoeCkgKyBleHAoLXgpKTtcbiAgfVxufSk7XG4iLCIvLyAyMC4yLjIuMzQgTWF0aC50cnVuYyh4KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywge1xuICB0cnVuYzogZnVuY3Rpb24gdHJ1bmMoaXQpIHtcbiAgICByZXR1cm4gKGl0ID4gMCA/IE1hdGguZmxvb3IgOiBNYXRoLmNlaWwpKGl0KTtcbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgZ2xvYmFsID0gcmVxdWlyZSgnLi9fZ2xvYmFsJyk7XG52YXIgaGFzID0gcmVxdWlyZSgnLi9faGFzJyk7XG52YXIgY29mID0gcmVxdWlyZSgnLi9fY29mJyk7XG52YXIgaW5oZXJpdElmUmVxdWlyZWQgPSByZXF1aXJlKCcuL19pbmhlcml0LWlmLXJlcXVpcmVkJyk7XG52YXIgdG9QcmltaXRpdmUgPSByZXF1aXJlKCcuL190by1wcmltaXRpdmUnKTtcbnZhciBmYWlscyA9IHJlcXVpcmUoJy4vX2ZhaWxzJyk7XG52YXIgZ09QTiA9IHJlcXVpcmUoJy4vX29iamVjdC1nb3BuJykuZjtcbnZhciBnT1BEID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcGQnKS5mO1xudmFyIGRQID0gcmVxdWlyZSgnLi9fb2JqZWN0LWRwJykuZjtcbnZhciAkdHJpbSA9IHJlcXVpcmUoJy4vX3N0cmluZy10cmltJykudHJpbTtcbnZhciBOVU1CRVIgPSAnTnVtYmVyJztcbnZhciAkTnVtYmVyID0gZ2xvYmFsW05VTUJFUl07XG52YXIgQmFzZSA9ICROdW1iZXI7XG52YXIgcHJvdG8gPSAkTnVtYmVyLnByb3RvdHlwZTtcbi8vIE9wZXJhIH4xMiBoYXMgYnJva2VuIE9iamVjdCN0b1N0cmluZ1xudmFyIEJST0tFTl9DT0YgPSBjb2YocmVxdWlyZSgnLi9fb2JqZWN0LWNyZWF0ZScpKHByb3RvKSkgPT0gTlVNQkVSO1xudmFyIFRSSU0gPSAndHJpbScgaW4gU3RyaW5nLnByb3RvdHlwZTtcblxuLy8gNy4xLjMgVG9OdW1iZXIoYXJndW1lbnQpXG52YXIgdG9OdW1iZXIgPSBmdW5jdGlvbiAoYXJndW1lbnQpIHtcbiAgdmFyIGl0ID0gdG9QcmltaXRpdmUoYXJndW1lbnQsIGZhbHNlKTtcbiAgaWYgKHR5cGVvZiBpdCA9PSAnc3RyaW5nJyAmJiBpdC5sZW5ndGggPiAyKSB7XG4gICAgaXQgPSBUUklNID8gaXQudHJpbSgpIDogJHRyaW0oaXQsIDMpO1xuICAgIHZhciBmaXJzdCA9IGl0LmNoYXJDb2RlQXQoMCk7XG4gICAgdmFyIHRoaXJkLCByYWRpeCwgbWF4Q29kZTtcbiAgICBpZiAoZmlyc3QgPT09IDQzIHx8IGZpcnN0ID09PSA0NSkge1xuICAgICAgdGhpcmQgPSBpdC5jaGFyQ29kZUF0KDIpO1xuICAgICAgaWYgKHRoaXJkID09PSA4OCB8fCB0aGlyZCA9PT0gMTIwKSByZXR1cm4gTmFOOyAvLyBOdW1iZXIoJysweDEnKSBzaG91bGQgYmUgTmFOLCBvbGQgVjggZml4XG4gICAgfSBlbHNlIGlmIChmaXJzdCA9PT0gNDgpIHtcbiAgICAgIHN3aXRjaCAoaXQuY2hhckNvZGVBdCgxKSkge1xuICAgICAgICBjYXNlIDY2OiBjYXNlIDk4OiByYWRpeCA9IDI7IG1heENvZGUgPSA0OTsgYnJlYWs7IC8vIGZhc3QgZXF1YWwgL14wYlswMV0rJC9pXG4gICAgICAgIGNhc2UgNzk6IGNhc2UgMTExOiByYWRpeCA9IDg7IG1heENvZGUgPSA1NTsgYnJlYWs7IC8vIGZhc3QgZXF1YWwgL14wb1swLTddKyQvaVxuICAgICAgICBkZWZhdWx0OiByZXR1cm4gK2l0O1xuICAgICAgfVxuICAgICAgZm9yICh2YXIgZGlnaXRzID0gaXQuc2xpY2UoMiksIGkgPSAwLCBsID0gZGlnaXRzLmxlbmd0aCwgY29kZTsgaSA8IGw7IGkrKykge1xuICAgICAgICBjb2RlID0gZGlnaXRzLmNoYXJDb2RlQXQoaSk7XG4gICAgICAgIC8vIHBhcnNlSW50IHBhcnNlcyBhIHN0cmluZyB0byBhIGZpcnN0IHVuYXZhaWxhYmxlIHN5bWJvbFxuICAgICAgICAvLyBidXQgVG9OdW1iZXIgc2hvdWxkIHJldHVybiBOYU4gaWYgYSBzdHJpbmcgY29udGFpbnMgdW5hdmFpbGFibGUgc3ltYm9sc1xuICAgICAgICBpZiAoY29kZSA8IDQ4IHx8IGNvZGUgPiBtYXhDb2RlKSByZXR1cm4gTmFOO1xuICAgICAgfSByZXR1cm4gcGFyc2VJbnQoZGlnaXRzLCByYWRpeCk7XG4gICAgfVxuICB9IHJldHVybiAraXQ7XG59O1xuXG5pZiAoISROdW1iZXIoJyAwbzEnKSB8fCAhJE51bWJlcignMGIxJykgfHwgJE51bWJlcignKzB4MScpKSB7XG4gICROdW1iZXIgPSBmdW5jdGlvbiBOdW1iZXIodmFsdWUpIHtcbiAgICB2YXIgaXQgPSBhcmd1bWVudHMubGVuZ3RoIDwgMSA/IDAgOiB2YWx1ZTtcbiAgICB2YXIgdGhhdCA9IHRoaXM7XG4gICAgcmV0dXJuIHRoYXQgaW5zdGFuY2VvZiAkTnVtYmVyXG4gICAgICAvLyBjaGVjayBvbiAxLi5jb25zdHJ1Y3Rvcihmb28pIGNhc2VcbiAgICAgICYmIChCUk9LRU5fQ09GID8gZmFpbHMoZnVuY3Rpb24gKCkgeyBwcm90by52YWx1ZU9mLmNhbGwodGhhdCk7IH0pIDogY29mKHRoYXQpICE9IE5VTUJFUilcbiAgICAgICAgPyBpbmhlcml0SWZSZXF1aXJlZChuZXcgQmFzZSh0b051bWJlcihpdCkpLCB0aGF0LCAkTnVtYmVyKSA6IHRvTnVtYmVyKGl0KTtcbiAgfTtcbiAgZm9yICh2YXIga2V5cyA9IHJlcXVpcmUoJy4vX2Rlc2NyaXB0b3JzJykgPyBnT1BOKEJhc2UpIDogKFxuICAgIC8vIEVTMzpcbiAgICAnTUFYX1ZBTFVFLE1JTl9WQUxVRSxOYU4sTkVHQVRJVkVfSU5GSU5JVFksUE9TSVRJVkVfSU5GSU5JVFksJyArXG4gICAgLy8gRVM2IChpbiBjYXNlLCBpZiBtb2R1bGVzIHdpdGggRVM2IE51bWJlciBzdGF0aWNzIHJlcXVpcmVkIGJlZm9yZSk6XG4gICAgJ0VQU0lMT04saXNGaW5pdGUsaXNJbnRlZ2VyLGlzTmFOLGlzU2FmZUludGVnZXIsTUFYX1NBRkVfSU5URUdFUiwnICtcbiAgICAnTUlOX1NBRkVfSU5URUdFUixwYXJzZUZsb2F0LHBhcnNlSW50LGlzSW50ZWdlcidcbiAgKS5zcGxpdCgnLCcpLCBqID0gMCwga2V5OyBrZXlzLmxlbmd0aCA+IGo7IGorKykge1xuICAgIGlmIChoYXMoQmFzZSwga2V5ID0ga2V5c1tqXSkgJiYgIWhhcygkTnVtYmVyLCBrZXkpKSB7XG4gICAgICBkUCgkTnVtYmVyLCBrZXksIGdPUEQoQmFzZSwga2V5KSk7XG4gICAgfVxuICB9XG4gICROdW1iZXIucHJvdG90eXBlID0gcHJvdG87XG4gIHByb3RvLmNvbnN0cnVjdG9yID0gJE51bWJlcjtcbiAgcmVxdWlyZSgnLi9fcmVkZWZpbmUnKShnbG9iYWwsIE5VTUJFUiwgJE51bWJlcik7XG59XG4iLCIvLyAyMC4xLjIuMSBOdW1iZXIuRVBTSUxPTlxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdOdW1iZXInLCB7IEVQU0lMT046IE1hdGgucG93KDIsIC01MikgfSk7XG4iLCIvLyAyMC4xLjIuMiBOdW1iZXIuaXNGaW5pdGUobnVtYmVyKVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBfaXNGaW5pdGUgPSByZXF1aXJlKCcuL19nbG9iYWwnKS5pc0Zpbml0ZTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdOdW1iZXInLCB7XG4gIGlzRmluaXRlOiBmdW5jdGlvbiBpc0Zpbml0ZShpdCkge1xuICAgIHJldHVybiB0eXBlb2YgaXQgPT0gJ251bWJlcicgJiYgX2lzRmluaXRlKGl0KTtcbiAgfVxufSk7XG4iLCIvLyAyMC4xLjIuMyBOdW1iZXIuaXNJbnRlZ2VyKG51bWJlcilcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTnVtYmVyJywgeyBpc0ludGVnZXI6IHJlcXVpcmUoJy4vX2lzLWludGVnZXInKSB9KTtcbiIsIi8vIDIwLjEuMi40IE51bWJlci5pc05hTihudW1iZXIpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ051bWJlcicsIHtcbiAgaXNOYU46IGZ1bmN0aW9uIGlzTmFOKG51bWJlcikge1xuICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1zZWxmLWNvbXBhcmVcbiAgICByZXR1cm4gbnVtYmVyICE9IG51bWJlcjtcbiAgfVxufSk7XG4iLCIvLyAyMC4xLjIuNSBOdW1iZXIuaXNTYWZlSW50ZWdlcihudW1iZXIpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGlzSW50ZWdlciA9IHJlcXVpcmUoJy4vX2lzLWludGVnZXInKTtcbnZhciBhYnMgPSBNYXRoLmFicztcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdOdW1iZXInLCB7XG4gIGlzU2FmZUludGVnZXI6IGZ1bmN0aW9uIGlzU2FmZUludGVnZXIobnVtYmVyKSB7XG4gICAgcmV0dXJuIGlzSW50ZWdlcihudW1iZXIpICYmIGFicyhudW1iZXIpIDw9IDB4MWZmZmZmZmZmZmZmZmY7XG4gIH1cbn0pO1xuIiwiLy8gMjAuMS4yLjYgTnVtYmVyLk1BWF9TQUZFX0lOVEVHRVJcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTnVtYmVyJywgeyBNQVhfU0FGRV9JTlRFR0VSOiAweDFmZmZmZmZmZmZmZmZmIH0pO1xuIiwiLy8gMjAuMS4yLjEwIE51bWJlci5NSU5fU0FGRV9JTlRFR0VSXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ051bWJlcicsIHsgTUlOX1NBRkVfSU5URUdFUjogLTB4MWZmZmZmZmZmZmZmZmYgfSk7XG4iLCJ2YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRwYXJzZUZsb2F0ID0gcmVxdWlyZSgnLi9fcGFyc2UtZmxvYXQnKTtcbi8vIDIwLjEuMi4xMiBOdW1iZXIucGFyc2VGbG9hdChzdHJpbmcpXG4kZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqIChOdW1iZXIucGFyc2VGbG9hdCAhPSAkcGFyc2VGbG9hdCksICdOdW1iZXInLCB7IHBhcnNlRmxvYXQ6ICRwYXJzZUZsb2F0IH0pO1xuIiwidmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciAkcGFyc2VJbnQgPSByZXF1aXJlKCcuL19wYXJzZS1pbnQnKTtcbi8vIDIwLjEuMi4xMyBOdW1iZXIucGFyc2VJbnQoc3RyaW5nLCByYWRpeClcbiRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogKE51bWJlci5wYXJzZUludCAhPSAkcGFyc2VJbnQpLCAnTnVtYmVyJywgeyBwYXJzZUludDogJHBhcnNlSW50IH0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciB0b0ludGVnZXIgPSByZXF1aXJlKCcuL190by1pbnRlZ2VyJyk7XG52YXIgYU51bWJlclZhbHVlID0gcmVxdWlyZSgnLi9fYS1udW1iZXItdmFsdWUnKTtcbnZhciByZXBlYXQgPSByZXF1aXJlKCcuL19zdHJpbmctcmVwZWF0Jyk7XG52YXIgJHRvRml4ZWQgPSAxLjAudG9GaXhlZDtcbnZhciBmbG9vciA9IE1hdGguZmxvb3I7XG52YXIgZGF0YSA9IFswLCAwLCAwLCAwLCAwLCAwXTtcbnZhciBFUlJPUiA9ICdOdW1iZXIudG9GaXhlZDogaW5jb3JyZWN0IGludm9jYXRpb24hJztcbnZhciBaRVJPID0gJzAnO1xuXG52YXIgbXVsdGlwbHkgPSBmdW5jdGlvbiAobiwgYykge1xuICB2YXIgaSA9IC0xO1xuICB2YXIgYzIgPSBjO1xuICB3aGlsZSAoKytpIDwgNikge1xuICAgIGMyICs9IG4gKiBkYXRhW2ldO1xuICAgIGRhdGFbaV0gPSBjMiAlIDFlNztcbiAgICBjMiA9IGZsb29yKGMyIC8gMWU3KTtcbiAgfVxufTtcbnZhciBkaXZpZGUgPSBmdW5jdGlvbiAobikge1xuICB2YXIgaSA9IDY7XG4gIHZhciBjID0gMDtcbiAgd2hpbGUgKC0taSA+PSAwKSB7XG4gICAgYyArPSBkYXRhW2ldO1xuICAgIGRhdGFbaV0gPSBmbG9vcihjIC8gbik7XG4gICAgYyA9IChjICUgbikgKiAxZTc7XG4gIH1cbn07XG52YXIgbnVtVG9TdHJpbmcgPSBmdW5jdGlvbiAoKSB7XG4gIHZhciBpID0gNjtcbiAgdmFyIHMgPSAnJztcbiAgd2hpbGUgKC0taSA+PSAwKSB7XG4gICAgaWYgKHMgIT09ICcnIHx8IGkgPT09IDAgfHwgZGF0YVtpXSAhPT0gMCkge1xuICAgICAgdmFyIHQgPSBTdHJpbmcoZGF0YVtpXSk7XG4gICAgICBzID0gcyA9PT0gJycgPyB0IDogcyArIHJlcGVhdC5jYWxsKFpFUk8sIDcgLSB0Lmxlbmd0aCkgKyB0O1xuICAgIH1cbiAgfSByZXR1cm4gcztcbn07XG52YXIgcG93ID0gZnVuY3Rpb24gKHgsIG4sIGFjYykge1xuICByZXR1cm4gbiA9PT0gMCA/IGFjYyA6IG4gJSAyID09PSAxID8gcG93KHgsIG4gLSAxLCBhY2MgKiB4KSA6IHBvdyh4ICogeCwgbiAvIDIsIGFjYyk7XG59O1xudmFyIGxvZyA9IGZ1bmN0aW9uICh4KSB7XG4gIHZhciBuID0gMDtcbiAgdmFyIHgyID0geDtcbiAgd2hpbGUgKHgyID49IDQwOTYpIHtcbiAgICBuICs9IDEyO1xuICAgIHgyIC89IDQwOTY7XG4gIH1cbiAgd2hpbGUgKHgyID49IDIpIHtcbiAgICBuICs9IDE7XG4gICAgeDIgLz0gMjtcbiAgfSByZXR1cm4gbjtcbn07XG5cbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogKCEhJHRvRml4ZWQgJiYgKFxuICAwLjAwMDA4LnRvRml4ZWQoMykgIT09ICcwLjAwMCcgfHxcbiAgMC45LnRvRml4ZWQoMCkgIT09ICcxJyB8fFxuICAxLjI1NS50b0ZpeGVkKDIpICE9PSAnMS4yNScgfHxcbiAgMTAwMDAwMDAwMDAwMDAwMDEyOC4wLnRvRml4ZWQoMCkgIT09ICcxMDAwMDAwMDAwMDAwMDAwMTI4J1xuKSB8fCAhcmVxdWlyZSgnLi9fZmFpbHMnKShmdW5jdGlvbiAoKSB7XG4gIC8vIFY4IH4gQW5kcm9pZCA0LjMtXG4gICR0b0ZpeGVkLmNhbGwoe30pO1xufSkpLCAnTnVtYmVyJywge1xuICB0b0ZpeGVkOiBmdW5jdGlvbiB0b0ZpeGVkKGZyYWN0aW9uRGlnaXRzKSB7XG4gICAgdmFyIHggPSBhTnVtYmVyVmFsdWUodGhpcywgRVJST1IpO1xuICAgIHZhciBmID0gdG9JbnRlZ2VyKGZyYWN0aW9uRGlnaXRzKTtcbiAgICB2YXIgcyA9ICcnO1xuICAgIHZhciBtID0gWkVSTztcbiAgICB2YXIgZSwgeiwgaiwgaztcbiAgICBpZiAoZiA8IDAgfHwgZiA+IDIwKSB0aHJvdyBSYW5nZUVycm9yKEVSUk9SKTtcbiAgICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tc2VsZi1jb21wYXJlXG4gICAgaWYgKHggIT0geCkgcmV0dXJuICdOYU4nO1xuICAgIGlmICh4IDw9IC0xZTIxIHx8IHggPj0gMWUyMSkgcmV0dXJuIFN0cmluZyh4KTtcbiAgICBpZiAoeCA8IDApIHtcbiAgICAgIHMgPSAnLSc7XG4gICAgICB4ID0gLXg7XG4gICAgfVxuICAgIGlmICh4ID4gMWUtMjEpIHtcbiAgICAgIGUgPSBsb2coeCAqIHBvdygyLCA2OSwgMSkpIC0gNjk7XG4gICAgICB6ID0gZSA8IDAgPyB4ICogcG93KDIsIC1lLCAxKSA6IHggLyBwb3coMiwgZSwgMSk7XG4gICAgICB6ICo9IDB4MTAwMDAwMDAwMDAwMDA7XG4gICAgICBlID0gNTIgLSBlO1xuICAgICAgaWYgKGUgPiAwKSB7XG4gICAgICAgIG11bHRpcGx5KDAsIHopO1xuICAgICAgICBqID0gZjtcbiAgICAgICAgd2hpbGUgKGogPj0gNykge1xuICAgICAgICAgIG11bHRpcGx5KDFlNywgMCk7XG4gICAgICAgICAgaiAtPSA3O1xuICAgICAgICB9XG4gICAgICAgIG11bHRpcGx5KHBvdygxMCwgaiwgMSksIDApO1xuICAgICAgICBqID0gZSAtIDE7XG4gICAgICAgIHdoaWxlIChqID49IDIzKSB7XG4gICAgICAgICAgZGl2aWRlKDEgPDwgMjMpO1xuICAgICAgICAgIGogLT0gMjM7XG4gICAgICAgIH1cbiAgICAgICAgZGl2aWRlKDEgPDwgaik7XG4gICAgICAgIG11bHRpcGx5KDEsIDEpO1xuICAgICAgICBkaXZpZGUoMik7XG4gICAgICAgIG0gPSBudW1Ub1N0cmluZygpO1xuICAgICAgfSBlbHNlIHtcbiAgICAgICAgbXVsdGlwbHkoMCwgeik7XG4gICAgICAgIG11bHRpcGx5KDEgPDwgLWUsIDApO1xuICAgICAgICBtID0gbnVtVG9TdHJpbmcoKSArIHJlcGVhdC5jYWxsKFpFUk8sIGYpO1xuICAgICAgfVxuICAgIH1cbiAgICBpZiAoZiA+IDApIHtcbiAgICAgIGsgPSBtLmxlbmd0aDtcbiAgICAgIG0gPSBzICsgKGsgPD0gZiA/ICcwLicgKyByZXBlYXQuY2FsbChaRVJPLCBmIC0gaykgKyBtIDogbS5zbGljZSgwLCBrIC0gZikgKyAnLicgKyBtLnNsaWNlKGsgLSBmKSk7XG4gICAgfSBlbHNlIHtcbiAgICAgIG0gPSBzICsgbTtcbiAgICB9IHJldHVybiBtO1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgJGZhaWxzID0gcmVxdWlyZSgnLi9fZmFpbHMnKTtcbnZhciBhTnVtYmVyVmFsdWUgPSByZXF1aXJlKCcuL19hLW51bWJlci12YWx1ZScpO1xudmFyICR0b1ByZWNpc2lvbiA9IDEuMC50b1ByZWNpc2lvbjtcblxuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAoJGZhaWxzKGZ1bmN0aW9uICgpIHtcbiAgLy8gSUU3LVxuICByZXR1cm4gJHRvUHJlY2lzaW9uLmNhbGwoMSwgdW5kZWZpbmVkKSAhPT0gJzEnO1xufSkgfHwgISRmYWlscyhmdW5jdGlvbiAoKSB7XG4gIC8vIFY4IH4gQW5kcm9pZCA0LjMtXG4gICR0b1ByZWNpc2lvbi5jYWxsKHt9KTtcbn0pKSwgJ051bWJlcicsIHtcbiAgdG9QcmVjaXNpb246IGZ1bmN0aW9uIHRvUHJlY2lzaW9uKHByZWNpc2lvbikge1xuICAgIHZhciB0aGF0ID0gYU51bWJlclZhbHVlKHRoaXMsICdOdW1iZXIjdG9QcmVjaXNpb246IGluY29ycmVjdCBpbnZvY2F0aW9uIScpO1xuICAgIHJldHVybiBwcmVjaXNpb24gPT09IHVuZGVmaW5lZCA/ICR0b1ByZWNpc2lvbi5jYWxsKHRoYXQpIDogJHRvUHJlY2lzaW9uLmNhbGwodGhhdCwgcHJlY2lzaW9uKTtcbiAgfVxufSk7XG4iLCIvLyAxOS4xLjMuMSBPYmplY3QuYXNzaWduKHRhcmdldCwgc291cmNlKVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYsICdPYmplY3QnLCB7IGFzc2lnbjogcmVxdWlyZSgnLi9fb2JqZWN0LWFzc2lnbicpIH0pO1xuIiwidmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbi8vIDE5LjEuMi4yIC8gMTUuMi4zLjUgT2JqZWN0LmNyZWF0ZShPIFssIFByb3BlcnRpZXNdKVxuJGV4cG9ydCgkZXhwb3J0LlMsICdPYmplY3QnLCB7IGNyZWF0ZTogcmVxdWlyZSgnLi9fb2JqZWN0LWNyZWF0ZScpIH0pO1xuIiwidmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbi8vIDE5LjEuMi4zIC8gMTUuMi4zLjcgT2JqZWN0LmRlZmluZVByb3BlcnRpZXMoTywgUHJvcGVydGllcylcbiRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogIXJlcXVpcmUoJy4vX2Rlc2NyaXB0b3JzJyksICdPYmplY3QnLCB7IGRlZmluZVByb3BlcnRpZXM6IHJlcXVpcmUoJy4vX29iamVjdC1kcHMnKSB9KTtcbiIsInZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG4vLyAxOS4xLjIuNCAvIDE1LjIuMy42IE9iamVjdC5kZWZpbmVQcm9wZXJ0eShPLCBQLCBBdHRyaWJ1dGVzKVxuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiAhcmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKSwgJ09iamVjdCcsIHsgZGVmaW5lUHJvcGVydHk6IHJlcXVpcmUoJy4vX29iamVjdC1kcCcpLmYgfSk7XG4iLCIvLyAxOS4xLjIuNSBPYmplY3QuZnJlZXplKE8pXG52YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcbnZhciBtZXRhID0gcmVxdWlyZSgnLi9fbWV0YScpLm9uRnJlZXplO1xuXG5yZXF1aXJlKCcuL19vYmplY3Qtc2FwJykoJ2ZyZWV6ZScsIGZ1bmN0aW9uICgkZnJlZXplKSB7XG4gIHJldHVybiBmdW5jdGlvbiBmcmVlemUoaXQpIHtcbiAgICByZXR1cm4gJGZyZWV6ZSAmJiBpc09iamVjdChpdCkgPyAkZnJlZXplKG1ldGEoaXQpKSA6IGl0O1xuICB9O1xufSk7XG4iLCIvLyAxOS4xLjIuNiBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKE8sIFApXG52YXIgdG9JT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8taW9iamVjdCcpO1xudmFyICRnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IgPSByZXF1aXJlKCcuL19vYmplY3QtZ29wZCcpLmY7XG5cbnJlcXVpcmUoJy4vX29iamVjdC1zYXAnKSgnZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yJywgZnVuY3Rpb24gKCkge1xuICByZXR1cm4gZnVuY3Rpb24gZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGl0LCBrZXkpIHtcbiAgICByZXR1cm4gJGdldE93blByb3BlcnR5RGVzY3JpcHRvcih0b0lPYmplY3QoaXQpLCBrZXkpO1xuICB9O1xufSk7XG4iLCIvLyAxOS4xLjIuNyBPYmplY3QuZ2V0T3duUHJvcGVydHlOYW1lcyhPKVxucmVxdWlyZSgnLi9fb2JqZWN0LXNhcCcpKCdnZXRPd25Qcm9wZXJ0eU5hbWVzJywgZnVuY3Rpb24gKCkge1xuICByZXR1cm4gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcG4tZXh0JykuZjtcbn0pO1xuIiwiLy8gMTkuMS4yLjkgT2JqZWN0LmdldFByb3RvdHlwZU9mKE8pXG52YXIgdG9PYmplY3QgPSByZXF1aXJlKCcuL190by1vYmplY3QnKTtcbnZhciAkZ2V0UHJvdG90eXBlT2YgPSByZXF1aXJlKCcuL19vYmplY3QtZ3BvJyk7XG5cbnJlcXVpcmUoJy4vX29iamVjdC1zYXAnKSgnZ2V0UHJvdG90eXBlT2YnLCBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBmdW5jdGlvbiBnZXRQcm90b3R5cGVPZihpdCkge1xuICAgIHJldHVybiAkZ2V0UHJvdG90eXBlT2YodG9PYmplY3QoaXQpKTtcbiAgfTtcbn0pO1xuIiwiLy8gMTkuMS4yLjExIE9iamVjdC5pc0V4dGVuc2libGUoTylcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xuXG5yZXF1aXJlKCcuL19vYmplY3Qtc2FwJykoJ2lzRXh0ZW5zaWJsZScsIGZ1bmN0aW9uICgkaXNFeHRlbnNpYmxlKSB7XG4gIHJldHVybiBmdW5jdGlvbiBpc0V4dGVuc2libGUoaXQpIHtcbiAgICByZXR1cm4gaXNPYmplY3QoaXQpID8gJGlzRXh0ZW5zaWJsZSA/ICRpc0V4dGVuc2libGUoaXQpIDogdHJ1ZSA6IGZhbHNlO1xuICB9O1xufSk7XG4iLCIvLyAxOS4xLjIuMTIgT2JqZWN0LmlzRnJvemVuKE8pXG52YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcblxucmVxdWlyZSgnLi9fb2JqZWN0LXNhcCcpKCdpc0Zyb3plbicsIGZ1bmN0aW9uICgkaXNGcm96ZW4pIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIGlzRnJvemVuKGl0KSB7XG4gICAgcmV0dXJuIGlzT2JqZWN0KGl0KSA/ICRpc0Zyb3plbiA/ICRpc0Zyb3plbihpdCkgOiBmYWxzZSA6IHRydWU7XG4gIH07XG59KTtcbiIsIi8vIDE5LjEuMi4xMyBPYmplY3QuaXNTZWFsZWQoTylcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xuXG5yZXF1aXJlKCcuL19vYmplY3Qtc2FwJykoJ2lzU2VhbGVkJywgZnVuY3Rpb24gKCRpc1NlYWxlZCkge1xuICByZXR1cm4gZnVuY3Rpb24gaXNTZWFsZWQoaXQpIHtcbiAgICByZXR1cm4gaXNPYmplY3QoaXQpID8gJGlzU2VhbGVkID8gJGlzU2VhbGVkKGl0KSA6IGZhbHNlIDogdHJ1ZTtcbiAgfTtcbn0pO1xuIiwiLy8gMTkuMS4zLjEwIE9iamVjdC5pcyh2YWx1ZTEsIHZhbHVlMilcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG4kZXhwb3J0KCRleHBvcnQuUywgJ09iamVjdCcsIHsgaXM6IHJlcXVpcmUoJy4vX3NhbWUtdmFsdWUnKSB9KTtcbiIsIi8vIDE5LjEuMi4xNCBPYmplY3Qua2V5cyhPKVxudmFyIHRvT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8tb2JqZWN0Jyk7XG52YXIgJGtleXMgPSByZXF1aXJlKCcuL19vYmplY3Qta2V5cycpO1xuXG5yZXF1aXJlKCcuL19vYmplY3Qtc2FwJykoJ2tleXMnLCBmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBmdW5jdGlvbiBrZXlzKGl0KSB7XG4gICAgcmV0dXJuICRrZXlzKHRvT2JqZWN0KGl0KSk7XG4gIH07XG59KTtcbiIsIi8vIDE5LjEuMi4xNSBPYmplY3QucHJldmVudEV4dGVuc2lvbnMoTylcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xudmFyIG1ldGEgPSByZXF1aXJlKCcuL19tZXRhJykub25GcmVlemU7XG5cbnJlcXVpcmUoJy4vX29iamVjdC1zYXAnKSgncHJldmVudEV4dGVuc2lvbnMnLCBmdW5jdGlvbiAoJHByZXZlbnRFeHRlbnNpb25zKSB7XG4gIHJldHVybiBmdW5jdGlvbiBwcmV2ZW50RXh0ZW5zaW9ucyhpdCkge1xuICAgIHJldHVybiAkcHJldmVudEV4dGVuc2lvbnMgJiYgaXNPYmplY3QoaXQpID8gJHByZXZlbnRFeHRlbnNpb25zKG1ldGEoaXQpKSA6IGl0O1xuICB9O1xufSk7XG4iLCIvLyAxOS4xLjIuMTcgT2JqZWN0LnNlYWwoTylcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xudmFyIG1ldGEgPSByZXF1aXJlKCcuL19tZXRhJykub25GcmVlemU7XG5cbnJlcXVpcmUoJy4vX29iamVjdC1zYXAnKSgnc2VhbCcsIGZ1bmN0aW9uICgkc2VhbCkge1xuICByZXR1cm4gZnVuY3Rpb24gc2VhbChpdCkge1xuICAgIHJldHVybiAkc2VhbCAmJiBpc09iamVjdChpdCkgPyAkc2VhbChtZXRhKGl0KSkgOiBpdDtcbiAgfTtcbn0pO1xuIiwiLy8gMTkuMS4zLjE5IE9iamVjdC5zZXRQcm90b3R5cGVPZihPLCBwcm90bylcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG4kZXhwb3J0KCRleHBvcnQuUywgJ09iamVjdCcsIHsgc2V0UHJvdG90eXBlT2Y6IHJlcXVpcmUoJy4vX3NldC1wcm90bycpLnNldCB9KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIDE5LjEuMy42IE9iamVjdC5wcm90b3R5cGUudG9TdHJpbmcoKVxudmFyIGNsYXNzb2YgPSByZXF1aXJlKCcuL19jbGFzc29mJyk7XG52YXIgdGVzdCA9IHt9O1xudGVzdFtyZXF1aXJlKCcuL193a3MnKSgndG9TdHJpbmdUYWcnKV0gPSAneic7XG5pZiAodGVzdCArICcnICE9ICdbb2JqZWN0IHpdJykge1xuICByZXF1aXJlKCcuL19yZWRlZmluZScpKE9iamVjdC5wcm90b3R5cGUsICd0b1N0cmluZycsIGZ1bmN0aW9uIHRvU3RyaW5nKCkge1xuICAgIHJldHVybiAnW29iamVjdCAnICsgY2xhc3NvZih0aGlzKSArICddJztcbiAgfSwgdHJ1ZSk7XG59XG4iLCJ2YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRwYXJzZUZsb2F0ID0gcmVxdWlyZSgnLi9fcGFyc2UtZmxvYXQnKTtcbi8vIDE4LjIuNCBwYXJzZUZsb2F0KHN0cmluZylcbiRleHBvcnQoJGV4cG9ydC5HICsgJGV4cG9ydC5GICogKHBhcnNlRmxvYXQgIT0gJHBhcnNlRmxvYXQpLCB7IHBhcnNlRmxvYXQ6ICRwYXJzZUZsb2F0IH0pO1xuIiwidmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciAkcGFyc2VJbnQgPSByZXF1aXJlKCcuL19wYXJzZS1pbnQnKTtcbi8vIDE4LjIuNSBwYXJzZUludChzdHJpbmcsIHJhZGl4KVxuJGV4cG9ydCgkZXhwb3J0LkcgKyAkZXhwb3J0LkYgKiAocGFyc2VJbnQgIT0gJHBhcnNlSW50KSwgeyBwYXJzZUludDogJHBhcnNlSW50IH0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIExJQlJBUlkgPSByZXF1aXJlKCcuL19saWJyYXJ5Jyk7XG52YXIgZ2xvYmFsID0gcmVxdWlyZSgnLi9fZ2xvYmFsJyk7XG52YXIgY3R4ID0gcmVxdWlyZSgnLi9fY3R4Jyk7XG52YXIgY2xhc3NvZiA9IHJlcXVpcmUoJy4vX2NsYXNzb2YnKTtcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcbnZhciBhRnVuY3Rpb24gPSByZXF1aXJlKCcuL19hLWZ1bmN0aW9uJyk7XG52YXIgYW5JbnN0YW5jZSA9IHJlcXVpcmUoJy4vX2FuLWluc3RhbmNlJyk7XG52YXIgZm9yT2YgPSByZXF1aXJlKCcuL19mb3Itb2YnKTtcbnZhciBzcGVjaWVzQ29uc3RydWN0b3IgPSByZXF1aXJlKCcuL19zcGVjaWVzLWNvbnN0cnVjdG9yJyk7XG52YXIgdGFzayA9IHJlcXVpcmUoJy4vX3Rhc2snKS5zZXQ7XG52YXIgbWljcm90YXNrID0gcmVxdWlyZSgnLi9fbWljcm90YXNrJykoKTtcbnZhciBuZXdQcm9taXNlQ2FwYWJpbGl0eU1vZHVsZSA9IHJlcXVpcmUoJy4vX25ldy1wcm9taXNlLWNhcGFiaWxpdHknKTtcbnZhciBwZXJmb3JtID0gcmVxdWlyZSgnLi9fcGVyZm9ybScpO1xudmFyIHVzZXJBZ2VudCA9IHJlcXVpcmUoJy4vX3VzZXItYWdlbnQnKTtcbnZhciBwcm9taXNlUmVzb2x2ZSA9IHJlcXVpcmUoJy4vX3Byb21pc2UtcmVzb2x2ZScpO1xudmFyIFBST01JU0UgPSAnUHJvbWlzZSc7XG52YXIgVHlwZUVycm9yID0gZ2xvYmFsLlR5cGVFcnJvcjtcbnZhciBwcm9jZXNzID0gZ2xvYmFsLnByb2Nlc3M7XG52YXIgdmVyc2lvbnMgPSBwcm9jZXNzICYmIHByb2Nlc3MudmVyc2lvbnM7XG52YXIgdjggPSB2ZXJzaW9ucyAmJiB2ZXJzaW9ucy52OCB8fCAnJztcbnZhciAkUHJvbWlzZSA9IGdsb2JhbFtQUk9NSVNFXTtcbnZhciBpc05vZGUgPSBjbGFzc29mKHByb2Nlc3MpID09ICdwcm9jZXNzJztcbnZhciBlbXB0eSA9IGZ1bmN0aW9uICgpIHsgLyogZW1wdHkgKi8gfTtcbnZhciBJbnRlcm5hbCwgbmV3R2VuZXJpY1Byb21pc2VDYXBhYmlsaXR5LCBPd25Qcm9taXNlQ2FwYWJpbGl0eSwgV3JhcHBlcjtcbnZhciBuZXdQcm9taXNlQ2FwYWJpbGl0eSA9IG5ld0dlbmVyaWNQcm9taXNlQ2FwYWJpbGl0eSA9IG5ld1Byb21pc2VDYXBhYmlsaXR5TW9kdWxlLmY7XG5cbnZhciBVU0VfTkFUSVZFID0gISFmdW5jdGlvbiAoKSB7XG4gIHRyeSB7XG4gICAgLy8gY29ycmVjdCBzdWJjbGFzc2luZyB3aXRoIEBAc3BlY2llcyBzdXBwb3J0XG4gICAgdmFyIHByb21pc2UgPSAkUHJvbWlzZS5yZXNvbHZlKDEpO1xuICAgIHZhciBGYWtlUHJvbWlzZSA9IChwcm9taXNlLmNvbnN0cnVjdG9yID0ge30pW3JlcXVpcmUoJy4vX3drcycpKCdzcGVjaWVzJyldID0gZnVuY3Rpb24gKGV4ZWMpIHtcbiAgICAgIGV4ZWMoZW1wdHksIGVtcHR5KTtcbiAgICB9O1xuICAgIC8vIHVuaGFuZGxlZCByZWplY3Rpb25zIHRyYWNraW5nIHN1cHBvcnQsIE5vZGVKUyBQcm9taXNlIHdpdGhvdXQgaXQgZmFpbHMgQEBzcGVjaWVzIHRlc3RcbiAgICByZXR1cm4gKGlzTm9kZSB8fCB0eXBlb2YgUHJvbWlzZVJlamVjdGlvbkV2ZW50ID09ICdmdW5jdGlvbicpXG4gICAgICAmJiBwcm9taXNlLnRoZW4oZW1wdHkpIGluc3RhbmNlb2YgRmFrZVByb21pc2VcbiAgICAgIC8vIHY4IDYuNiAoTm9kZSAxMCBhbmQgQ2hyb21lIDY2KSBoYXZlIGEgYnVnIHdpdGggcmVzb2x2aW5nIGN1c3RvbSB0aGVuYWJsZXNcbiAgICAgIC8vIGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC9jaHJvbWl1bS9pc3N1ZXMvZGV0YWlsP2lkPTgzMDU2NVxuICAgICAgLy8gd2UgY2FuJ3QgZGV0ZWN0IGl0IHN5bmNocm9ub3VzbHksIHNvIGp1c3QgY2hlY2sgdmVyc2lvbnNcbiAgICAgICYmIHY4LmluZGV4T2YoJzYuNicpICE9PSAwXG4gICAgICAmJiB1c2VyQWdlbnQuaW5kZXhPZignQ2hyb21lLzY2JykgPT09IC0xO1xuICB9IGNhdGNoIChlKSB7IC8qIGVtcHR5ICovIH1cbn0oKTtcblxuLy8gaGVscGVyc1xudmFyIGlzVGhlbmFibGUgPSBmdW5jdGlvbiAoaXQpIHtcbiAgdmFyIHRoZW47XG4gIHJldHVybiBpc09iamVjdChpdCkgJiYgdHlwZW9mICh0aGVuID0gaXQudGhlbikgPT0gJ2Z1bmN0aW9uJyA/IHRoZW4gOiBmYWxzZTtcbn07XG52YXIgbm90aWZ5ID0gZnVuY3Rpb24gKHByb21pc2UsIGlzUmVqZWN0KSB7XG4gIGlmIChwcm9taXNlLl9uKSByZXR1cm47XG4gIHByb21pc2UuX24gPSB0cnVlO1xuICB2YXIgY2hhaW4gPSBwcm9taXNlLl9jO1xuICBtaWNyb3Rhc2soZnVuY3Rpb24gKCkge1xuICAgIHZhciB2YWx1ZSA9IHByb21pc2UuX3Y7XG4gICAgdmFyIG9rID0gcHJvbWlzZS5fcyA9PSAxO1xuICAgIHZhciBpID0gMDtcbiAgICB2YXIgcnVuID0gZnVuY3Rpb24gKHJlYWN0aW9uKSB7XG4gICAgICB2YXIgaGFuZGxlciA9IG9rID8gcmVhY3Rpb24ub2sgOiByZWFjdGlvbi5mYWlsO1xuICAgICAgdmFyIHJlc29sdmUgPSByZWFjdGlvbi5yZXNvbHZlO1xuICAgICAgdmFyIHJlamVjdCA9IHJlYWN0aW9uLnJlamVjdDtcbiAgICAgIHZhciBkb21haW4gPSByZWFjdGlvbi5kb21haW47XG4gICAgICB2YXIgcmVzdWx0LCB0aGVuLCBleGl0ZWQ7XG4gICAgICB0cnkge1xuICAgICAgICBpZiAoaGFuZGxlcikge1xuICAgICAgICAgIGlmICghb2spIHtcbiAgICAgICAgICAgIGlmIChwcm9taXNlLl9oID09IDIpIG9uSGFuZGxlVW5oYW5kbGVkKHByb21pc2UpO1xuICAgICAgICAgICAgcHJvbWlzZS5faCA9IDE7XG4gICAgICAgICAgfVxuICAgICAgICAgIGlmIChoYW5kbGVyID09PSB0cnVlKSByZXN1bHQgPSB2YWx1ZTtcbiAgICAgICAgICBlbHNlIHtcbiAgICAgICAgICAgIGlmIChkb21haW4pIGRvbWFpbi5lbnRlcigpO1xuICAgICAgICAgICAgcmVzdWx0ID0gaGFuZGxlcih2YWx1ZSk7IC8vIG1heSB0aHJvd1xuICAgICAgICAgICAgaWYgKGRvbWFpbikge1xuICAgICAgICAgICAgICBkb21haW4uZXhpdCgpO1xuICAgICAgICAgICAgICBleGl0ZWQgPSB0cnVlO1xuICAgICAgICAgICAgfVxuICAgICAgICAgIH1cbiAgICAgICAgICBpZiAocmVzdWx0ID09PSByZWFjdGlvbi5wcm9taXNlKSB7XG4gICAgICAgICAgICByZWplY3QoVHlwZUVycm9yKCdQcm9taXNlLWNoYWluIGN5Y2xlJykpO1xuICAgICAgICAgIH0gZWxzZSBpZiAodGhlbiA9IGlzVGhlbmFibGUocmVzdWx0KSkge1xuICAgICAgICAgICAgdGhlbi5jYWxsKHJlc3VsdCwgcmVzb2x2ZSwgcmVqZWN0KTtcbiAgICAgICAgICB9IGVsc2UgcmVzb2x2ZShyZXN1bHQpO1xuICAgICAgICB9IGVsc2UgcmVqZWN0KHZhbHVlKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgaWYgKGRvbWFpbiAmJiAhZXhpdGVkKSBkb21haW4uZXhpdCgpO1xuICAgICAgICByZWplY3QoZSk7XG4gICAgICB9XG4gICAgfTtcbiAgICB3aGlsZSAoY2hhaW4ubGVuZ3RoID4gaSkgcnVuKGNoYWluW2krK10pOyAvLyB2YXJpYWJsZSBsZW5ndGggLSBjYW4ndCB1c2UgZm9yRWFjaFxuICAgIHByb21pc2UuX2MgPSBbXTtcbiAgICBwcm9taXNlLl9uID0gZmFsc2U7XG4gICAgaWYgKGlzUmVqZWN0ICYmICFwcm9taXNlLl9oKSBvblVuaGFuZGxlZChwcm9taXNlKTtcbiAgfSk7XG59O1xudmFyIG9uVW5oYW5kbGVkID0gZnVuY3Rpb24gKHByb21pc2UpIHtcbiAgdGFzay5jYWxsKGdsb2JhbCwgZnVuY3Rpb24gKCkge1xuICAgIHZhciB2YWx1ZSA9IHByb21pc2UuX3Y7XG4gICAgdmFyIHVuaGFuZGxlZCA9IGlzVW5oYW5kbGVkKHByb21pc2UpO1xuICAgIHZhciByZXN1bHQsIGhhbmRsZXIsIGNvbnNvbGU7XG4gICAgaWYgKHVuaGFuZGxlZCkge1xuICAgICAgcmVzdWx0ID0gcGVyZm9ybShmdW5jdGlvbiAoKSB7XG4gICAgICAgIGlmIChpc05vZGUpIHtcbiAgICAgICAgICBwcm9jZXNzLmVtaXQoJ3VuaGFuZGxlZFJlamVjdGlvbicsIHZhbHVlLCBwcm9taXNlKTtcbiAgICAgICAgfSBlbHNlIGlmIChoYW5kbGVyID0gZ2xvYmFsLm9udW5oYW5kbGVkcmVqZWN0aW9uKSB7XG4gICAgICAgICAgaGFuZGxlcih7IHByb21pc2U6IHByb21pc2UsIHJlYXNvbjogdmFsdWUgfSk7XG4gICAgICAgIH0gZWxzZSBpZiAoKGNvbnNvbGUgPSBnbG9iYWwuY29uc29sZSkgJiYgY29uc29sZS5lcnJvcikge1xuICAgICAgICAgIGNvbnNvbGUuZXJyb3IoJ1VuaGFuZGxlZCBwcm9taXNlIHJlamVjdGlvbicsIHZhbHVlKTtcbiAgICAgICAgfVxuICAgICAgfSk7XG4gICAgICAvLyBCcm93c2VycyBzaG91bGQgbm90IHRyaWdnZXIgYHJlamVjdGlvbkhhbmRsZWRgIGV2ZW50IGlmIGl0IHdhcyBoYW5kbGVkIGhlcmUsIE5vZGVKUyAtIHNob3VsZFxuICAgICAgcHJvbWlzZS5faCA9IGlzTm9kZSB8fCBpc1VuaGFuZGxlZChwcm9taXNlKSA/IDIgOiAxO1xuICAgIH0gcHJvbWlzZS5fYSA9IHVuZGVmaW5lZDtcbiAgICBpZiAodW5oYW5kbGVkICYmIHJlc3VsdC5lKSB0aHJvdyByZXN1bHQudjtcbiAgfSk7XG59O1xudmFyIGlzVW5oYW5kbGVkID0gZnVuY3Rpb24gKHByb21pc2UpIHtcbiAgcmV0dXJuIHByb21pc2UuX2ggIT09IDEgJiYgKHByb21pc2UuX2EgfHwgcHJvbWlzZS5fYykubGVuZ3RoID09PSAwO1xufTtcbnZhciBvbkhhbmRsZVVuaGFuZGxlZCA9IGZ1bmN0aW9uIChwcm9taXNlKSB7XG4gIHRhc2suY2FsbChnbG9iYWwsIGZ1bmN0aW9uICgpIHtcbiAgICB2YXIgaGFuZGxlcjtcbiAgICBpZiAoaXNOb2RlKSB7XG4gICAgICBwcm9jZXNzLmVtaXQoJ3JlamVjdGlvbkhhbmRsZWQnLCBwcm9taXNlKTtcbiAgICB9IGVsc2UgaWYgKGhhbmRsZXIgPSBnbG9iYWwub25yZWplY3Rpb25oYW5kbGVkKSB7XG4gICAgICBoYW5kbGVyKHsgcHJvbWlzZTogcHJvbWlzZSwgcmVhc29uOiBwcm9taXNlLl92IH0pO1xuICAgIH1cbiAgfSk7XG59O1xudmFyICRyZWplY3QgPSBmdW5jdGlvbiAodmFsdWUpIHtcbiAgdmFyIHByb21pc2UgPSB0aGlzO1xuICBpZiAocHJvbWlzZS5fZCkgcmV0dXJuO1xuICBwcm9taXNlLl9kID0gdHJ1ZTtcbiAgcHJvbWlzZSA9IHByb21pc2UuX3cgfHwgcHJvbWlzZTsgLy8gdW53cmFwXG4gIHByb21pc2UuX3YgPSB2YWx1ZTtcbiAgcHJvbWlzZS5fcyA9IDI7XG4gIGlmICghcHJvbWlzZS5fYSkgcHJvbWlzZS5fYSA9IHByb21pc2UuX2Muc2xpY2UoKTtcbiAgbm90aWZ5KHByb21pc2UsIHRydWUpO1xufTtcbnZhciAkcmVzb2x2ZSA9IGZ1bmN0aW9uICh2YWx1ZSkge1xuICB2YXIgcHJvbWlzZSA9IHRoaXM7XG4gIHZhciB0aGVuO1xuICBpZiAocHJvbWlzZS5fZCkgcmV0dXJuO1xuICBwcm9taXNlLl9kID0gdHJ1ZTtcbiAgcHJvbWlzZSA9IHByb21pc2UuX3cgfHwgcHJvbWlzZTsgLy8gdW53cmFwXG4gIHRyeSB7XG4gICAgaWYgKHByb21pc2UgPT09IHZhbHVlKSB0aHJvdyBUeXBlRXJyb3IoXCJQcm9taXNlIGNhbid0IGJlIHJlc29sdmVkIGl0c2VsZlwiKTtcbiAgICBpZiAodGhlbiA9IGlzVGhlbmFibGUodmFsdWUpKSB7XG4gICAgICBtaWNyb3Rhc2soZnVuY3Rpb24gKCkge1xuICAgICAgICB2YXIgd3JhcHBlciA9IHsgX3c6IHByb21pc2UsIF9kOiBmYWxzZSB9OyAvLyB3cmFwXG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgdGhlbi5jYWxsKHZhbHVlLCBjdHgoJHJlc29sdmUsIHdyYXBwZXIsIDEpLCBjdHgoJHJlamVjdCwgd3JhcHBlciwgMSkpO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgJHJlamVjdC5jYWxsKHdyYXBwZXIsIGUpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICB9IGVsc2Uge1xuICAgICAgcHJvbWlzZS5fdiA9IHZhbHVlO1xuICAgICAgcHJvbWlzZS5fcyA9IDE7XG4gICAgICBub3RpZnkocHJvbWlzZSwgZmFsc2UpO1xuICAgIH1cbiAgfSBjYXRjaCAoZSkge1xuICAgICRyZWplY3QuY2FsbCh7IF93OiBwcm9taXNlLCBfZDogZmFsc2UgfSwgZSk7IC8vIHdyYXBcbiAgfVxufTtcblxuLy8gY29uc3RydWN0b3IgcG9seWZpbGxcbmlmICghVVNFX05BVElWRSkge1xuICAvLyAyNS40LjMuMSBQcm9taXNlKGV4ZWN1dG9yKVxuICAkUHJvbWlzZSA9IGZ1bmN0aW9uIFByb21pc2UoZXhlY3V0b3IpIHtcbiAgICBhbkluc3RhbmNlKHRoaXMsICRQcm9taXNlLCBQUk9NSVNFLCAnX2gnKTtcbiAgICBhRnVuY3Rpb24oZXhlY3V0b3IpO1xuICAgIEludGVybmFsLmNhbGwodGhpcyk7XG4gICAgdHJ5IHtcbiAgICAgIGV4ZWN1dG9yKGN0eCgkcmVzb2x2ZSwgdGhpcywgMSksIGN0eCgkcmVqZWN0LCB0aGlzLCAxKSk7XG4gICAgfSBjYXRjaCAoZXJyKSB7XG4gICAgICAkcmVqZWN0LmNhbGwodGhpcywgZXJyKTtcbiAgICB9XG4gIH07XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby11bnVzZWQtdmFyc1xuICBJbnRlcm5hbCA9IGZ1bmN0aW9uIFByb21pc2UoZXhlY3V0b3IpIHtcbiAgICB0aGlzLl9jID0gW107ICAgICAgICAgICAgIC8vIDwtIGF3YWl0aW5nIHJlYWN0aW9uc1xuICAgIHRoaXMuX2EgPSB1bmRlZmluZWQ7ICAgICAgLy8gPC0gY2hlY2tlZCBpbiBpc1VuaGFuZGxlZCByZWFjdGlvbnNcbiAgICB0aGlzLl9zID0gMDsgICAgICAgICAgICAgIC8vIDwtIHN0YXRlXG4gICAgdGhpcy5fZCA9IGZhbHNlOyAgICAgICAgICAvLyA8LSBkb25lXG4gICAgdGhpcy5fdiA9IHVuZGVmaW5lZDsgICAgICAvLyA8LSB2YWx1ZVxuICAgIHRoaXMuX2ggPSAwOyAgICAgICAgICAgICAgLy8gPC0gcmVqZWN0aW9uIHN0YXRlLCAwIC0gZGVmYXVsdCwgMSAtIGhhbmRsZWQsIDIgLSB1bmhhbmRsZWRcbiAgICB0aGlzLl9uID0gZmFsc2U7ICAgICAgICAgIC8vIDwtIG5vdGlmeVxuICB9O1xuICBJbnRlcm5hbC5wcm90b3R5cGUgPSByZXF1aXJlKCcuL19yZWRlZmluZS1hbGwnKSgkUHJvbWlzZS5wcm90b3R5cGUsIHtcbiAgICAvLyAyNS40LjUuMyBQcm9taXNlLnByb3RvdHlwZS50aGVuKG9uRnVsZmlsbGVkLCBvblJlamVjdGVkKVxuICAgIHRoZW46IGZ1bmN0aW9uIHRoZW4ob25GdWxmaWxsZWQsIG9uUmVqZWN0ZWQpIHtcbiAgICAgIHZhciByZWFjdGlvbiA9IG5ld1Byb21pc2VDYXBhYmlsaXR5KHNwZWNpZXNDb25zdHJ1Y3Rvcih0aGlzLCAkUHJvbWlzZSkpO1xuICAgICAgcmVhY3Rpb24ub2sgPSB0eXBlb2Ygb25GdWxmaWxsZWQgPT0gJ2Z1bmN0aW9uJyA/IG9uRnVsZmlsbGVkIDogdHJ1ZTtcbiAgICAgIHJlYWN0aW9uLmZhaWwgPSB0eXBlb2Ygb25SZWplY3RlZCA9PSAnZnVuY3Rpb24nICYmIG9uUmVqZWN0ZWQ7XG4gICAgICByZWFjdGlvbi5kb21haW4gPSBpc05vZGUgPyBwcm9jZXNzLmRvbWFpbiA6IHVuZGVmaW5lZDtcbiAgICAgIHRoaXMuX2MucHVzaChyZWFjdGlvbik7XG4gICAgICBpZiAodGhpcy5fYSkgdGhpcy5fYS5wdXNoKHJlYWN0aW9uKTtcbiAgICAgIGlmICh0aGlzLl9zKSBub3RpZnkodGhpcywgZmFsc2UpO1xuICAgICAgcmV0dXJuIHJlYWN0aW9uLnByb21pc2U7XG4gICAgfSxcbiAgICAvLyAyNS40LjUuMSBQcm9taXNlLnByb3RvdHlwZS5jYXRjaChvblJlamVjdGVkKVxuICAgICdjYXRjaCc6IGZ1bmN0aW9uIChvblJlamVjdGVkKSB7XG4gICAgICByZXR1cm4gdGhpcy50aGVuKHVuZGVmaW5lZCwgb25SZWplY3RlZCk7XG4gICAgfVxuICB9KTtcbiAgT3duUHJvbWlzZUNhcGFiaWxpdHkgPSBmdW5jdGlvbiAoKSB7XG4gICAgdmFyIHByb21pc2UgPSBuZXcgSW50ZXJuYWwoKTtcbiAgICB0aGlzLnByb21pc2UgPSBwcm9taXNlO1xuICAgIHRoaXMucmVzb2x2ZSA9IGN0eCgkcmVzb2x2ZSwgcHJvbWlzZSwgMSk7XG4gICAgdGhpcy5yZWplY3QgPSBjdHgoJHJlamVjdCwgcHJvbWlzZSwgMSk7XG4gIH07XG4gIG5ld1Byb21pc2VDYXBhYmlsaXR5TW9kdWxlLmYgPSBuZXdQcm9taXNlQ2FwYWJpbGl0eSA9IGZ1bmN0aW9uIChDKSB7XG4gICAgcmV0dXJuIEMgPT09ICRQcm9taXNlIHx8IEMgPT09IFdyYXBwZXJcbiAgICAgID8gbmV3IE93blByb21pc2VDYXBhYmlsaXR5KEMpXG4gICAgICA6IG5ld0dlbmVyaWNQcm9taXNlQ2FwYWJpbGl0eShDKTtcbiAgfTtcbn1cblxuJGV4cG9ydCgkZXhwb3J0LkcgKyAkZXhwb3J0LlcgKyAkZXhwb3J0LkYgKiAhVVNFX05BVElWRSwgeyBQcm9taXNlOiAkUHJvbWlzZSB9KTtcbnJlcXVpcmUoJy4vX3NldC10by1zdHJpbmctdGFnJykoJFByb21pc2UsIFBST01JU0UpO1xucmVxdWlyZSgnLi9fc2V0LXNwZWNpZXMnKShQUk9NSVNFKTtcbldyYXBwZXIgPSByZXF1aXJlKCcuL19jb3JlJylbUFJPTUlTRV07XG5cbi8vIHN0YXRpY3NcbiRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogIVVTRV9OQVRJVkUsIFBST01JU0UsIHtcbiAgLy8gMjUuNC40LjUgUHJvbWlzZS5yZWplY3QocilcbiAgcmVqZWN0OiBmdW5jdGlvbiByZWplY3Qocikge1xuICAgIHZhciBjYXBhYmlsaXR5ID0gbmV3UHJvbWlzZUNhcGFiaWxpdHkodGhpcyk7XG4gICAgdmFyICQkcmVqZWN0ID0gY2FwYWJpbGl0eS5yZWplY3Q7XG4gICAgJCRyZWplY3Qocik7XG4gICAgcmV0dXJuIGNhcGFiaWxpdHkucHJvbWlzZTtcbiAgfVxufSk7XG4kZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqIChMSUJSQVJZIHx8ICFVU0VfTkFUSVZFKSwgUFJPTUlTRSwge1xuICAvLyAyNS40LjQuNiBQcm9taXNlLnJlc29sdmUoeClcbiAgcmVzb2x2ZTogZnVuY3Rpb24gcmVzb2x2ZSh4KSB7XG4gICAgcmV0dXJuIHByb21pc2VSZXNvbHZlKExJQlJBUlkgJiYgdGhpcyA9PT0gV3JhcHBlciA/ICRQcm9taXNlIDogdGhpcywgeCk7XG4gIH1cbn0pO1xuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiAhKFVTRV9OQVRJVkUgJiYgcmVxdWlyZSgnLi9faXRlci1kZXRlY3QnKShmdW5jdGlvbiAoaXRlcikge1xuICAkUHJvbWlzZS5hbGwoaXRlcilbJ2NhdGNoJ10oZW1wdHkpO1xufSkpLCBQUk9NSVNFLCB7XG4gIC8vIDI1LjQuNC4xIFByb21pc2UuYWxsKGl0ZXJhYmxlKVxuICBhbGw6IGZ1bmN0aW9uIGFsbChpdGVyYWJsZSkge1xuICAgIHZhciBDID0gdGhpcztcbiAgICB2YXIgY2FwYWJpbGl0eSA9IG5ld1Byb21pc2VDYXBhYmlsaXR5KEMpO1xuICAgIHZhciByZXNvbHZlID0gY2FwYWJpbGl0eS5yZXNvbHZlO1xuICAgIHZhciByZWplY3QgPSBjYXBhYmlsaXR5LnJlamVjdDtcbiAgICB2YXIgcmVzdWx0ID0gcGVyZm9ybShmdW5jdGlvbiAoKSB7XG4gICAgICB2YXIgdmFsdWVzID0gW107XG4gICAgICB2YXIgaW5kZXggPSAwO1xuICAgICAgdmFyIHJlbWFpbmluZyA9IDE7XG4gICAgICBmb3JPZihpdGVyYWJsZSwgZmFsc2UsIGZ1bmN0aW9uIChwcm9taXNlKSB7XG4gICAgICAgIHZhciAkaW5kZXggPSBpbmRleCsrO1xuICAgICAgICB2YXIgYWxyZWFkeUNhbGxlZCA9IGZhbHNlO1xuICAgICAgICB2YWx1ZXMucHVzaCh1bmRlZmluZWQpO1xuICAgICAgICByZW1haW5pbmcrKztcbiAgICAgICAgQy5yZXNvbHZlKHByb21pc2UpLnRoZW4oZnVuY3Rpb24gKHZhbHVlKSB7XG4gICAgICAgICAgaWYgKGFscmVhZHlDYWxsZWQpIHJldHVybjtcbiAgICAgICAgICBhbHJlYWR5Q2FsbGVkID0gdHJ1ZTtcbiAgICAgICAgICB2YWx1ZXNbJGluZGV4XSA9IHZhbHVlO1xuICAgICAgICAgIC0tcmVtYWluaW5nIHx8IHJlc29sdmUodmFsdWVzKTtcbiAgICAgICAgfSwgcmVqZWN0KTtcbiAgICAgIH0pO1xuICAgICAgLS1yZW1haW5pbmcgfHwgcmVzb2x2ZSh2YWx1ZXMpO1xuICAgIH0pO1xuICAgIGlmIChyZXN1bHQuZSkgcmVqZWN0KHJlc3VsdC52KTtcbiAgICByZXR1cm4gY2FwYWJpbGl0eS5wcm9taXNlO1xuICB9LFxuICAvLyAyNS40LjQuNCBQcm9taXNlLnJhY2UoaXRlcmFibGUpXG4gIHJhY2U6IGZ1bmN0aW9uIHJhY2UoaXRlcmFibGUpIHtcbiAgICB2YXIgQyA9IHRoaXM7XG4gICAgdmFyIGNhcGFiaWxpdHkgPSBuZXdQcm9taXNlQ2FwYWJpbGl0eShDKTtcbiAgICB2YXIgcmVqZWN0ID0gY2FwYWJpbGl0eS5yZWplY3Q7XG4gICAgdmFyIHJlc3VsdCA9IHBlcmZvcm0oZnVuY3Rpb24gKCkge1xuICAgICAgZm9yT2YoaXRlcmFibGUsIGZhbHNlLCBmdW5jdGlvbiAocHJvbWlzZSkge1xuICAgICAgICBDLnJlc29sdmUocHJvbWlzZSkudGhlbihjYXBhYmlsaXR5LnJlc29sdmUsIHJlamVjdCk7XG4gICAgICB9KTtcbiAgICB9KTtcbiAgICBpZiAocmVzdWx0LmUpIHJlamVjdChyZXN1bHQudik7XG4gICAgcmV0dXJuIGNhcGFiaWxpdHkucHJvbWlzZTtcbiAgfVxufSk7XG4iLCIvLyAyNi4xLjEgUmVmbGVjdC5hcHBseSh0YXJnZXQsIHRoaXNBcmd1bWVudCwgYXJndW1lbnRzTGlzdClcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgYUZ1bmN0aW9uID0gcmVxdWlyZSgnLi9fYS1mdW5jdGlvbicpO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgckFwcGx5ID0gKHJlcXVpcmUoJy4vX2dsb2JhbCcpLlJlZmxlY3QgfHwge30pLmFwcGx5O1xudmFyIGZBcHBseSA9IEZ1bmN0aW9uLmFwcGx5O1xuLy8gTVMgRWRnZSBhcmd1bWVudHNMaXN0IGFyZ3VtZW50IGlzIG9wdGlvbmFsXG4kZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqICFyZXF1aXJlKCcuL19mYWlscycpKGZ1bmN0aW9uICgpIHtcbiAgckFwcGx5KGZ1bmN0aW9uICgpIHsgLyogZW1wdHkgKi8gfSk7XG59KSwgJ1JlZmxlY3QnLCB7XG4gIGFwcGx5OiBmdW5jdGlvbiBhcHBseSh0YXJnZXQsIHRoaXNBcmd1bWVudCwgYXJndW1lbnRzTGlzdCkge1xuICAgIHZhciBUID0gYUZ1bmN0aW9uKHRhcmdldCk7XG4gICAgdmFyIEwgPSBhbk9iamVjdChhcmd1bWVudHNMaXN0KTtcbiAgICByZXR1cm4gckFwcGx5ID8gckFwcGx5KFQsIHRoaXNBcmd1bWVudCwgTCkgOiBmQXBwbHkuY2FsbChULCB0aGlzQXJndW1lbnQsIEwpO1xuICB9XG59KTtcbiIsIi8vIDI2LjEuMiBSZWZsZWN0LmNvbnN0cnVjdCh0YXJnZXQsIGFyZ3VtZW50c0xpc3QgWywgbmV3VGFyZ2V0XSlcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgY3JlYXRlID0gcmVxdWlyZSgnLi9fb2JqZWN0LWNyZWF0ZScpO1xudmFyIGFGdW5jdGlvbiA9IHJlcXVpcmUoJy4vX2EtZnVuY3Rpb24nKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIGlzT2JqZWN0ID0gcmVxdWlyZSgnLi9faXMtb2JqZWN0Jyk7XG52YXIgZmFpbHMgPSByZXF1aXJlKCcuL19mYWlscycpO1xudmFyIGJpbmQgPSByZXF1aXJlKCcuL19iaW5kJyk7XG52YXIgckNvbnN0cnVjdCA9IChyZXF1aXJlKCcuL19nbG9iYWwnKS5SZWZsZWN0IHx8IHt9KS5jb25zdHJ1Y3Q7XG5cbi8vIE1TIEVkZ2Ugc3VwcG9ydHMgb25seSAyIGFyZ3VtZW50cyBhbmQgYXJndW1lbnRzTGlzdCBhcmd1bWVudCBpcyBvcHRpb25hbFxuLy8gRkYgTmlnaHRseSBzZXRzIHRoaXJkIGFyZ3VtZW50IGFzIGBuZXcudGFyZ2V0YCwgYnV0IGRvZXMgbm90IGNyZWF0ZSBgdGhpc2AgZnJvbSBpdFxudmFyIE5FV19UQVJHRVRfQlVHID0gZmFpbHMoZnVuY3Rpb24gKCkge1xuICBmdW5jdGlvbiBGKCkgeyAvKiBlbXB0eSAqLyB9XG4gIHJldHVybiAhKHJDb25zdHJ1Y3QoZnVuY3Rpb24gKCkgeyAvKiBlbXB0eSAqLyB9LCBbXSwgRikgaW5zdGFuY2VvZiBGKTtcbn0pO1xudmFyIEFSR1NfQlVHID0gIWZhaWxzKGZ1bmN0aW9uICgpIHtcbiAgckNvbnN0cnVjdChmdW5jdGlvbiAoKSB7IC8qIGVtcHR5ICovIH0pO1xufSk7XG5cbiRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogKE5FV19UQVJHRVRfQlVHIHx8IEFSR1NfQlVHKSwgJ1JlZmxlY3QnLCB7XG4gIGNvbnN0cnVjdDogZnVuY3Rpb24gY29uc3RydWN0KFRhcmdldCwgYXJncyAvKiAsIG5ld1RhcmdldCAqLykge1xuICAgIGFGdW5jdGlvbihUYXJnZXQpO1xuICAgIGFuT2JqZWN0KGFyZ3MpO1xuICAgIHZhciBuZXdUYXJnZXQgPSBhcmd1bWVudHMubGVuZ3RoIDwgMyA/IFRhcmdldCA6IGFGdW5jdGlvbihhcmd1bWVudHNbMl0pO1xuICAgIGlmIChBUkdTX0JVRyAmJiAhTkVXX1RBUkdFVF9CVUcpIHJldHVybiByQ29uc3RydWN0KFRhcmdldCwgYXJncywgbmV3VGFyZ2V0KTtcbiAgICBpZiAoVGFyZ2V0ID09IG5ld1RhcmdldCkge1xuICAgICAgLy8gdy9vIGFsdGVyZWQgbmV3VGFyZ2V0LCBvcHRpbWl6YXRpb24gZm9yIDAtNCBhcmd1bWVudHNcbiAgICAgIHN3aXRjaCAoYXJncy5sZW5ndGgpIHtcbiAgICAgICAgY2FzZSAwOiByZXR1cm4gbmV3IFRhcmdldCgpO1xuICAgICAgICBjYXNlIDE6IHJldHVybiBuZXcgVGFyZ2V0KGFyZ3NbMF0pO1xuICAgICAgICBjYXNlIDI6IHJldHVybiBuZXcgVGFyZ2V0KGFyZ3NbMF0sIGFyZ3NbMV0pO1xuICAgICAgICBjYXNlIDM6IHJldHVybiBuZXcgVGFyZ2V0KGFyZ3NbMF0sIGFyZ3NbMV0sIGFyZ3NbMl0pO1xuICAgICAgICBjYXNlIDQ6IHJldHVybiBuZXcgVGFyZ2V0KGFyZ3NbMF0sIGFyZ3NbMV0sIGFyZ3NbMl0sIGFyZ3NbM10pO1xuICAgICAgfVxuICAgICAgLy8gdy9vIGFsdGVyZWQgbmV3VGFyZ2V0LCBsb3Qgb2YgYXJndW1lbnRzIGNhc2VcbiAgICAgIHZhciAkYXJncyA9IFtudWxsXTtcbiAgICAgICRhcmdzLnB1c2guYXBwbHkoJGFyZ3MsIGFyZ3MpO1xuICAgICAgcmV0dXJuIG5ldyAoYmluZC5hcHBseShUYXJnZXQsICRhcmdzKSkoKTtcbiAgICB9XG4gICAgLy8gd2l0aCBhbHRlcmVkIG5ld1RhcmdldCwgbm90IHN1cHBvcnQgYnVpbHQtaW4gY29uc3RydWN0b3JzXG4gICAgdmFyIHByb3RvID0gbmV3VGFyZ2V0LnByb3RvdHlwZTtcbiAgICB2YXIgaW5zdGFuY2UgPSBjcmVhdGUoaXNPYmplY3QocHJvdG8pID8gcHJvdG8gOiBPYmplY3QucHJvdG90eXBlKTtcbiAgICB2YXIgcmVzdWx0ID0gRnVuY3Rpb24uYXBwbHkuY2FsbChUYXJnZXQsIGluc3RhbmNlLCBhcmdzKTtcbiAgICByZXR1cm4gaXNPYmplY3QocmVzdWx0KSA/IHJlc3VsdCA6IGluc3RhbmNlO1xuICB9XG59KTtcbiIsIi8vIDI2LjEuMyBSZWZsZWN0LmRlZmluZVByb3BlcnR5KHRhcmdldCwgcHJvcGVydHlLZXksIGF0dHJpYnV0ZXMpXG52YXIgZFAgPSByZXF1aXJlKCcuL19vYmplY3QtZHAnKTtcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciB0b1ByaW1pdGl2ZSA9IHJlcXVpcmUoJy4vX3RvLXByaW1pdGl2ZScpO1xuXG4vLyBNUyBFZGdlIGhhcyBicm9rZW4gUmVmbGVjdC5kZWZpbmVQcm9wZXJ0eSAtIHRocm93aW5nIGluc3RlYWQgb2YgcmV0dXJuaW5nIGZhbHNlXG4kZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqIHJlcXVpcmUoJy4vX2ZhaWxzJykoZnVuY3Rpb24gKCkge1xuICAvLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgbm8tdW5kZWZcbiAgUmVmbGVjdC5kZWZpbmVQcm9wZXJ0eShkUC5mKHt9LCAxLCB7IHZhbHVlOiAxIH0pLCAxLCB7IHZhbHVlOiAyIH0pO1xufSksICdSZWZsZWN0Jywge1xuICBkZWZpbmVQcm9wZXJ0eTogZnVuY3Rpb24gZGVmaW5lUHJvcGVydHkodGFyZ2V0LCBwcm9wZXJ0eUtleSwgYXR0cmlidXRlcykge1xuICAgIGFuT2JqZWN0KHRhcmdldCk7XG4gICAgcHJvcGVydHlLZXkgPSB0b1ByaW1pdGl2ZShwcm9wZXJ0eUtleSwgdHJ1ZSk7XG4gICAgYW5PYmplY3QoYXR0cmlidXRlcyk7XG4gICAgdHJ5IHtcbiAgICAgIGRQLmYodGFyZ2V0LCBwcm9wZXJ0eUtleSwgYXR0cmlidXRlcyk7XG4gICAgICByZXR1cm4gdHJ1ZTtcbiAgICB9IGNhdGNoIChlKSB7XG4gICAgICByZXR1cm4gZmFsc2U7XG4gICAgfVxuICB9XG59KTtcbiIsIi8vIDI2LjEuNCBSZWZsZWN0LmRlbGV0ZVByb3BlcnR5KHRhcmdldCwgcHJvcGVydHlLZXkpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGdPUEQgPSByZXF1aXJlKCcuL19vYmplY3QtZ29wZCcpLmY7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdSZWZsZWN0Jywge1xuICBkZWxldGVQcm9wZXJ0eTogZnVuY3Rpb24gZGVsZXRlUHJvcGVydHkodGFyZ2V0LCBwcm9wZXJ0eUtleSkge1xuICAgIHZhciBkZXNjID0gZ09QRChhbk9iamVjdCh0YXJnZXQpLCBwcm9wZXJ0eUtleSk7XG4gICAgcmV0dXJuIGRlc2MgJiYgIWRlc2MuY29uZmlndXJhYmxlID8gZmFsc2UgOiBkZWxldGUgdGFyZ2V0W3Byb3BlcnR5S2V5XTtcbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyAyNi4xLjUgUmVmbGVjdC5lbnVtZXJhdGUodGFyZ2V0KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIEVudW1lcmF0ZSA9IGZ1bmN0aW9uIChpdGVyYXRlZCkge1xuICB0aGlzLl90ID0gYW5PYmplY3QoaXRlcmF0ZWQpOyAvLyB0YXJnZXRcbiAgdGhpcy5faSA9IDA7ICAgICAgICAgICAgICAgICAgLy8gbmV4dCBpbmRleFxuICB2YXIga2V5cyA9IHRoaXMuX2sgPSBbXTsgICAgICAvLyBrZXlzXG4gIHZhciBrZXk7XG4gIGZvciAoa2V5IGluIGl0ZXJhdGVkKSBrZXlzLnB1c2goa2V5KTtcbn07XG5yZXF1aXJlKCcuL19pdGVyLWNyZWF0ZScpKEVudW1lcmF0ZSwgJ09iamVjdCcsIGZ1bmN0aW9uICgpIHtcbiAgdmFyIHRoYXQgPSB0aGlzO1xuICB2YXIga2V5cyA9IHRoYXQuX2s7XG4gIHZhciBrZXk7XG4gIGRvIHtcbiAgICBpZiAodGhhdC5faSA+PSBrZXlzLmxlbmd0aCkgcmV0dXJuIHsgdmFsdWU6IHVuZGVmaW5lZCwgZG9uZTogdHJ1ZSB9O1xuICB9IHdoaWxlICghKChrZXkgPSBrZXlzW3RoYXQuX2krK10pIGluIHRoYXQuX3QpKTtcbiAgcmV0dXJuIHsgdmFsdWU6IGtleSwgZG9uZTogZmFsc2UgfTtcbn0pO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ1JlZmxlY3QnLCB7XG4gIGVudW1lcmF0ZTogZnVuY3Rpb24gZW51bWVyYXRlKHRhcmdldCkge1xuICAgIHJldHVybiBuZXcgRW51bWVyYXRlKHRhcmdldCk7XG4gIH1cbn0pO1xuIiwiLy8gMjYuMS43IFJlZmxlY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKHRhcmdldCwgcHJvcGVydHlLZXkpXG52YXIgZ09QRCA9IHJlcXVpcmUoJy4vX29iamVjdC1nb3BkJyk7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnUmVmbGVjdCcsIHtcbiAgZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yOiBmdW5jdGlvbiBnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3IodGFyZ2V0LCBwcm9wZXJ0eUtleSkge1xuICAgIHJldHVybiBnT1BELmYoYW5PYmplY3QodGFyZ2V0KSwgcHJvcGVydHlLZXkpO1xuICB9XG59KTtcbiIsIi8vIDI2LjEuOCBSZWZsZWN0LmdldFByb3RvdHlwZU9mKHRhcmdldClcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgZ2V0UHJvdG8gPSByZXF1aXJlKCcuL19vYmplY3QtZ3BvJyk7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdSZWZsZWN0Jywge1xuICBnZXRQcm90b3R5cGVPZjogZnVuY3Rpb24gZ2V0UHJvdG90eXBlT2YodGFyZ2V0KSB7XG4gICAgcmV0dXJuIGdldFByb3RvKGFuT2JqZWN0KHRhcmdldCkpO1xuICB9XG59KTtcbiIsIi8vIDI2LjEuNiBSZWZsZWN0LmdldCh0YXJnZXQsIHByb3BlcnR5S2V5IFssIHJlY2VpdmVyXSlcbnZhciBnT1BEID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcGQnKTtcbnZhciBnZXRQcm90b3R5cGVPZiA9IHJlcXVpcmUoJy4vX29iamVjdC1ncG8nKTtcbnZhciBoYXMgPSByZXF1aXJlKCcuL19oYXMnKTtcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xuXG5mdW5jdGlvbiBnZXQodGFyZ2V0LCBwcm9wZXJ0eUtleSAvKiAsIHJlY2VpdmVyICovKSB7XG4gIHZhciByZWNlaXZlciA9IGFyZ3VtZW50cy5sZW5ndGggPCAzID8gdGFyZ2V0IDogYXJndW1lbnRzWzJdO1xuICB2YXIgZGVzYywgcHJvdG87XG4gIGlmIChhbk9iamVjdCh0YXJnZXQpID09PSByZWNlaXZlcikgcmV0dXJuIHRhcmdldFtwcm9wZXJ0eUtleV07XG4gIGlmIChkZXNjID0gZ09QRC5mKHRhcmdldCwgcHJvcGVydHlLZXkpKSByZXR1cm4gaGFzKGRlc2MsICd2YWx1ZScpXG4gICAgPyBkZXNjLnZhbHVlXG4gICAgOiBkZXNjLmdldCAhPT0gdW5kZWZpbmVkXG4gICAgICA/IGRlc2MuZ2V0LmNhbGwocmVjZWl2ZXIpXG4gICAgICA6IHVuZGVmaW5lZDtcbiAgaWYgKGlzT2JqZWN0KHByb3RvID0gZ2V0UHJvdG90eXBlT2YodGFyZ2V0KSkpIHJldHVybiBnZXQocHJvdG8sIHByb3BlcnR5S2V5LCByZWNlaXZlcik7XG59XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnUmVmbGVjdCcsIHsgZ2V0OiBnZXQgfSk7XG4iLCIvLyAyNi4xLjkgUmVmbGVjdC5oYXModGFyZ2V0LCBwcm9wZXJ0eUtleSlcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnUmVmbGVjdCcsIHtcbiAgaGFzOiBmdW5jdGlvbiBoYXModGFyZ2V0LCBwcm9wZXJ0eUtleSkge1xuICAgIHJldHVybiBwcm9wZXJ0eUtleSBpbiB0YXJnZXQ7XG4gIH1cbn0pO1xuIiwiLy8gMjYuMS4xMCBSZWZsZWN0LmlzRXh0ZW5zaWJsZSh0YXJnZXQpXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgJGlzRXh0ZW5zaWJsZSA9IE9iamVjdC5pc0V4dGVuc2libGU7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnUmVmbGVjdCcsIHtcbiAgaXNFeHRlbnNpYmxlOiBmdW5jdGlvbiBpc0V4dGVuc2libGUodGFyZ2V0KSB7XG4gICAgYW5PYmplY3QodGFyZ2V0KTtcbiAgICByZXR1cm4gJGlzRXh0ZW5zaWJsZSA/ICRpc0V4dGVuc2libGUodGFyZ2V0KSA6IHRydWU7XG4gIH1cbn0pO1xuIiwiLy8gMjYuMS4xMSBSZWZsZWN0Lm93bktleXModGFyZ2V0KVxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdSZWZsZWN0JywgeyBvd25LZXlzOiByZXF1aXJlKCcuL19vd24ta2V5cycpIH0pO1xuIiwiLy8gMjYuMS4xMiBSZWZsZWN0LnByZXZlbnRFeHRlbnNpb25zKHRhcmdldClcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciAkcHJldmVudEV4dGVuc2lvbnMgPSBPYmplY3QucHJldmVudEV4dGVuc2lvbnM7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnUmVmbGVjdCcsIHtcbiAgcHJldmVudEV4dGVuc2lvbnM6IGZ1bmN0aW9uIHByZXZlbnRFeHRlbnNpb25zKHRhcmdldCkge1xuICAgIGFuT2JqZWN0KHRhcmdldCk7XG4gICAgdHJ5IHtcbiAgICAgIGlmICgkcHJldmVudEV4dGVuc2lvbnMpICRwcmV2ZW50RXh0ZW5zaW9ucyh0YXJnZXQpO1xuICAgICAgcmV0dXJuIHRydWU7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgcmV0dXJuIGZhbHNlO1xuICAgIH1cbiAgfVxufSk7XG4iLCIvLyAyNi4xLjE0IFJlZmxlY3Quc2V0UHJvdG90eXBlT2YodGFyZ2V0LCBwcm90bylcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgc2V0UHJvdG8gPSByZXF1aXJlKCcuL19zZXQtcHJvdG8nKTtcblxuaWYgKHNldFByb3RvKSAkZXhwb3J0KCRleHBvcnQuUywgJ1JlZmxlY3QnLCB7XG4gIHNldFByb3RvdHlwZU9mOiBmdW5jdGlvbiBzZXRQcm90b3R5cGVPZih0YXJnZXQsIHByb3RvKSB7XG4gICAgc2V0UHJvdG8uY2hlY2sodGFyZ2V0LCBwcm90byk7XG4gICAgdHJ5IHtcbiAgICAgIHNldFByb3RvLnNldCh0YXJnZXQsIHByb3RvKTtcbiAgICAgIHJldHVybiB0cnVlO1xuICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgIHJldHVybiBmYWxzZTtcbiAgICB9XG4gIH1cbn0pO1xuIiwiLy8gMjYuMS4xMyBSZWZsZWN0LnNldCh0YXJnZXQsIHByb3BlcnR5S2V5LCBWIFssIHJlY2VpdmVyXSlcbnZhciBkUCA9IHJlcXVpcmUoJy4vX29iamVjdC1kcCcpO1xudmFyIGdPUEQgPSByZXF1aXJlKCcuL19vYmplY3QtZ29wZCcpO1xudmFyIGdldFByb3RvdHlwZU9mID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdwbycpO1xudmFyIGhhcyA9IHJlcXVpcmUoJy4vX2hhcycpO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBjcmVhdGVEZXNjID0gcmVxdWlyZSgnLi9fcHJvcGVydHktZGVzYycpO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcblxuZnVuY3Rpb24gc2V0KHRhcmdldCwgcHJvcGVydHlLZXksIFYgLyogLCByZWNlaXZlciAqLykge1xuICB2YXIgcmVjZWl2ZXIgPSBhcmd1bWVudHMubGVuZ3RoIDwgNCA/IHRhcmdldCA6IGFyZ3VtZW50c1szXTtcbiAgdmFyIG93bkRlc2MgPSBnT1BELmYoYW5PYmplY3QodGFyZ2V0KSwgcHJvcGVydHlLZXkpO1xuICB2YXIgZXhpc3RpbmdEZXNjcmlwdG9yLCBwcm90bztcbiAgaWYgKCFvd25EZXNjKSB7XG4gICAgaWYgKGlzT2JqZWN0KHByb3RvID0gZ2V0UHJvdG90eXBlT2YodGFyZ2V0KSkpIHtcbiAgICAgIHJldHVybiBzZXQocHJvdG8sIHByb3BlcnR5S2V5LCBWLCByZWNlaXZlcik7XG4gICAgfVxuICAgIG93bkRlc2MgPSBjcmVhdGVEZXNjKDApO1xuICB9XG4gIGlmIChoYXMob3duRGVzYywgJ3ZhbHVlJykpIHtcbiAgICBpZiAob3duRGVzYy53cml0YWJsZSA9PT0gZmFsc2UgfHwgIWlzT2JqZWN0KHJlY2VpdmVyKSkgcmV0dXJuIGZhbHNlO1xuICAgIGlmIChleGlzdGluZ0Rlc2NyaXB0b3IgPSBnT1BELmYocmVjZWl2ZXIsIHByb3BlcnR5S2V5KSkge1xuICAgICAgaWYgKGV4aXN0aW5nRGVzY3JpcHRvci5nZXQgfHwgZXhpc3RpbmdEZXNjcmlwdG9yLnNldCB8fCBleGlzdGluZ0Rlc2NyaXB0b3Iud3JpdGFibGUgPT09IGZhbHNlKSByZXR1cm4gZmFsc2U7XG4gICAgICBleGlzdGluZ0Rlc2NyaXB0b3IudmFsdWUgPSBWO1xuICAgICAgZFAuZihyZWNlaXZlciwgcHJvcGVydHlLZXksIGV4aXN0aW5nRGVzY3JpcHRvcik7XG4gICAgfSBlbHNlIGRQLmYocmVjZWl2ZXIsIHByb3BlcnR5S2V5LCBjcmVhdGVEZXNjKDAsIFYpKTtcbiAgICByZXR1cm4gdHJ1ZTtcbiAgfVxuICByZXR1cm4gb3duRGVzYy5zZXQgPT09IHVuZGVmaW5lZCA/IGZhbHNlIDogKG93bkRlc2Muc2V0LmNhbGwocmVjZWl2ZXIsIFYpLCB0cnVlKTtcbn1cblxuJGV4cG9ydCgkZXhwb3J0LlMsICdSZWZsZWN0JywgeyBzZXQ6IHNldCB9KTtcbiIsInZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciBpbmhlcml0SWZSZXF1aXJlZCA9IHJlcXVpcmUoJy4vX2luaGVyaXQtaWYtcmVxdWlyZWQnKTtcbnZhciBkUCA9IHJlcXVpcmUoJy4vX29iamVjdC1kcCcpLmY7XG52YXIgZ09QTiA9IHJlcXVpcmUoJy4vX29iamVjdC1nb3BuJykuZjtcbnZhciBpc1JlZ0V4cCA9IHJlcXVpcmUoJy4vX2lzLXJlZ2V4cCcpO1xudmFyICRmbGFncyA9IHJlcXVpcmUoJy4vX2ZsYWdzJyk7XG52YXIgJFJlZ0V4cCA9IGdsb2JhbC5SZWdFeHA7XG52YXIgQmFzZSA9ICRSZWdFeHA7XG52YXIgcHJvdG8gPSAkUmVnRXhwLnByb3RvdHlwZTtcbnZhciByZTEgPSAvYS9nO1xudmFyIHJlMiA9IC9hL2c7XG4vLyBcIm5ld1wiIGNyZWF0ZXMgYSBuZXcgb2JqZWN0LCBvbGQgd2Via2l0IGJ1Z2d5IGhlcmVcbnZhciBDT1JSRUNUX05FVyA9IG5ldyAkUmVnRXhwKHJlMSkgIT09IHJlMTtcblxuaWYgKHJlcXVpcmUoJy4vX2Rlc2NyaXB0b3JzJykgJiYgKCFDT1JSRUNUX05FVyB8fCByZXF1aXJlKCcuL19mYWlscycpKGZ1bmN0aW9uICgpIHtcbiAgcmUyW3JlcXVpcmUoJy4vX3drcycpKCdtYXRjaCcpXSA9IGZhbHNlO1xuICAvLyBSZWdFeHAgY29uc3RydWN0b3IgY2FuIGFsdGVyIGZsYWdzIGFuZCBJc1JlZ0V4cCB3b3JrcyBjb3JyZWN0IHdpdGggQEBtYXRjaFxuICByZXR1cm4gJFJlZ0V4cChyZTEpICE9IHJlMSB8fCAkUmVnRXhwKHJlMikgPT0gcmUyIHx8ICRSZWdFeHAocmUxLCAnaScpICE9ICcvYS9pJztcbn0pKSkge1xuICAkUmVnRXhwID0gZnVuY3Rpb24gUmVnRXhwKHAsIGYpIHtcbiAgICB2YXIgdGlSRSA9IHRoaXMgaW5zdGFuY2VvZiAkUmVnRXhwO1xuICAgIHZhciBwaVJFID0gaXNSZWdFeHAocCk7XG4gICAgdmFyIGZpVSA9IGYgPT09IHVuZGVmaW5lZDtcbiAgICByZXR1cm4gIXRpUkUgJiYgcGlSRSAmJiBwLmNvbnN0cnVjdG9yID09PSAkUmVnRXhwICYmIGZpVSA/IHBcbiAgICAgIDogaW5oZXJpdElmUmVxdWlyZWQoQ09SUkVDVF9ORVdcbiAgICAgICAgPyBuZXcgQmFzZShwaVJFICYmICFmaVUgPyBwLnNvdXJjZSA6IHAsIGYpXG4gICAgICAgIDogQmFzZSgocGlSRSA9IHAgaW5zdGFuY2VvZiAkUmVnRXhwKSA/IHAuc291cmNlIDogcCwgcGlSRSAmJiBmaVUgPyAkZmxhZ3MuY2FsbChwKSA6IGYpXG4gICAgICAsIHRpUkUgPyB0aGlzIDogcHJvdG8sICRSZWdFeHApO1xuICB9O1xuICB2YXIgcHJveHkgPSBmdW5jdGlvbiAoa2V5KSB7XG4gICAga2V5IGluICRSZWdFeHAgfHwgZFAoJFJlZ0V4cCwga2V5LCB7XG4gICAgICBjb25maWd1cmFibGU6IHRydWUsXG4gICAgICBnZXQ6IGZ1bmN0aW9uICgpIHsgcmV0dXJuIEJhc2Vba2V5XTsgfSxcbiAgICAgIHNldDogZnVuY3Rpb24gKGl0KSB7IEJhc2Vba2V5XSA9IGl0OyB9XG4gICAgfSk7XG4gIH07XG4gIGZvciAodmFyIGtleXMgPSBnT1BOKEJhc2UpLCBpID0gMDsga2V5cy5sZW5ndGggPiBpOykgcHJveHkoa2V5c1tpKytdKTtcbiAgcHJvdG8uY29uc3RydWN0b3IgPSAkUmVnRXhwO1xuICAkUmVnRXhwLnByb3RvdHlwZSA9IHByb3RvO1xuICByZXF1aXJlKCcuL19yZWRlZmluZScpKGdsb2JhbCwgJ1JlZ0V4cCcsICRSZWdFeHApO1xufVxuXG5yZXF1aXJlKCcuL19zZXQtc3BlY2llcycpKCdSZWdFeHAnKTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciByZWdleHBFeGVjID0gcmVxdWlyZSgnLi9fcmVnZXhwLWV4ZWMnKTtcbnJlcXVpcmUoJy4vX2V4cG9ydCcpKHtcbiAgdGFyZ2V0OiAnUmVnRXhwJyxcbiAgcHJvdG86IHRydWUsXG4gIGZvcmNlZDogcmVnZXhwRXhlYyAhPT0gLy4vLmV4ZWNcbn0sIHtcbiAgZXhlYzogcmVnZXhwRXhlY1xufSk7XG4iLCIvLyAyMS4yLjUuMyBnZXQgUmVnRXhwLnByb3RvdHlwZS5mbGFncygpXG5pZiAocmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKSAmJiAvLi9nLmZsYWdzICE9ICdnJykgcmVxdWlyZSgnLi9fb2JqZWN0LWRwJykuZihSZWdFeHAucHJvdG90eXBlLCAnZmxhZ3MnLCB7XG4gIGNvbmZpZ3VyYWJsZTogdHJ1ZSxcbiAgZ2V0OiByZXF1aXJlKCcuL19mbGFncycpXG59KTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgdG9MZW5ndGggPSByZXF1aXJlKCcuL190by1sZW5ndGgnKTtcbnZhciBhZHZhbmNlU3RyaW5nSW5kZXggPSByZXF1aXJlKCcuL19hZHZhbmNlLXN0cmluZy1pbmRleCcpO1xudmFyIHJlZ0V4cEV4ZWMgPSByZXF1aXJlKCcuL19yZWdleHAtZXhlYy1hYnN0cmFjdCcpO1xuXG4vLyBAQG1hdGNoIGxvZ2ljXG5yZXF1aXJlKCcuL19maXgtcmUtd2tzJykoJ21hdGNoJywgMSwgZnVuY3Rpb24gKGRlZmluZWQsIE1BVENILCAkbWF0Y2gsIG1heWJlQ2FsbE5hdGl2ZSkge1xuICByZXR1cm4gW1xuICAgIC8vIGBTdHJpbmcucHJvdG90eXBlLm1hdGNoYCBtZXRob2RcbiAgICAvLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL2VjbWEyNjIvI3NlYy1zdHJpbmcucHJvdG90eXBlLm1hdGNoXG4gICAgZnVuY3Rpb24gbWF0Y2gocmVnZXhwKSB7XG4gICAgICB2YXIgTyA9IGRlZmluZWQodGhpcyk7XG4gICAgICB2YXIgZm4gPSByZWdleHAgPT0gdW5kZWZpbmVkID8gdW5kZWZpbmVkIDogcmVnZXhwW01BVENIXTtcbiAgICAgIHJldHVybiBmbiAhPT0gdW5kZWZpbmVkID8gZm4uY2FsbChyZWdleHAsIE8pIDogbmV3IFJlZ0V4cChyZWdleHApW01BVENIXShTdHJpbmcoTykpO1xuICAgIH0sXG4gICAgLy8gYFJlZ0V4cC5wcm90b3R5cGVbQEBtYXRjaF1gIG1ldGhvZFxuICAgIC8vIGh0dHBzOi8vdGMzOS5naXRodWIuaW8vZWNtYTI2Mi8jc2VjLXJlZ2V4cC5wcm90b3R5cGUtQEBtYXRjaFxuICAgIGZ1bmN0aW9uIChyZWdleHApIHtcbiAgICAgIHZhciByZXMgPSBtYXliZUNhbGxOYXRpdmUoJG1hdGNoLCByZWdleHAsIHRoaXMpO1xuICAgICAgaWYgKHJlcy5kb25lKSByZXR1cm4gcmVzLnZhbHVlO1xuICAgICAgdmFyIHJ4ID0gYW5PYmplY3QocmVnZXhwKTtcbiAgICAgIHZhciBTID0gU3RyaW5nKHRoaXMpO1xuICAgICAgaWYgKCFyeC5nbG9iYWwpIHJldHVybiByZWdFeHBFeGVjKHJ4LCBTKTtcbiAgICAgIHZhciBmdWxsVW5pY29kZSA9IHJ4LnVuaWNvZGU7XG4gICAgICByeC5sYXN0SW5kZXggPSAwO1xuICAgICAgdmFyIEEgPSBbXTtcbiAgICAgIHZhciBuID0gMDtcbiAgICAgIHZhciByZXN1bHQ7XG4gICAgICB3aGlsZSAoKHJlc3VsdCA9IHJlZ0V4cEV4ZWMocngsIFMpKSAhPT0gbnVsbCkge1xuICAgICAgICB2YXIgbWF0Y2hTdHIgPSBTdHJpbmcocmVzdWx0WzBdKTtcbiAgICAgICAgQVtuXSA9IG1hdGNoU3RyO1xuICAgICAgICBpZiAobWF0Y2hTdHIgPT09ICcnKSByeC5sYXN0SW5kZXggPSBhZHZhbmNlU3RyaW5nSW5kZXgoUywgdG9MZW5ndGgocngubGFzdEluZGV4KSwgZnVsbFVuaWNvZGUpO1xuICAgICAgICBuKys7XG4gICAgICB9XG4gICAgICByZXR1cm4gbiA9PT0gMCA/IG51bGwgOiBBO1xuICAgIH1cbiAgXTtcbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xuXG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG52YXIgdG9JbnRlZ2VyID0gcmVxdWlyZSgnLi9fdG8taW50ZWdlcicpO1xudmFyIGFkdmFuY2VTdHJpbmdJbmRleCA9IHJlcXVpcmUoJy4vX2FkdmFuY2Utc3RyaW5nLWluZGV4Jyk7XG52YXIgcmVnRXhwRXhlYyA9IHJlcXVpcmUoJy4vX3JlZ2V4cC1leGVjLWFic3RyYWN0Jyk7XG52YXIgbWF4ID0gTWF0aC5tYXg7XG52YXIgbWluID0gTWF0aC5taW47XG52YXIgZmxvb3IgPSBNYXRoLmZsb29yO1xudmFyIFNVQlNUSVRVVElPTl9TWU1CT0xTID0gL1xcJChbJCZgJ118XFxkXFxkP3w8W14+XSo+KS9nO1xudmFyIFNVQlNUSVRVVElPTl9TWU1CT0xTX05PX05BTUVEID0gL1xcJChbJCZgJ118XFxkXFxkPykvZztcblxudmFyIG1heWJlVG9TdHJpbmcgPSBmdW5jdGlvbiAoaXQpIHtcbiAgcmV0dXJuIGl0ID09PSB1bmRlZmluZWQgPyBpdCA6IFN0cmluZyhpdCk7XG59O1xuXG4vLyBAQHJlcGxhY2UgbG9naWNcbnJlcXVpcmUoJy4vX2ZpeC1yZS13a3MnKSgncmVwbGFjZScsIDIsIGZ1bmN0aW9uIChkZWZpbmVkLCBSRVBMQUNFLCAkcmVwbGFjZSwgbWF5YmVDYWxsTmF0aXZlKSB7XG4gIHJldHVybiBbXG4gICAgLy8gYFN0cmluZy5wcm90b3R5cGUucmVwbGFjZWAgbWV0aG9kXG4gICAgLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9lY21hMjYyLyNzZWMtc3RyaW5nLnByb3RvdHlwZS5yZXBsYWNlXG4gICAgZnVuY3Rpb24gcmVwbGFjZShzZWFyY2hWYWx1ZSwgcmVwbGFjZVZhbHVlKSB7XG4gICAgICB2YXIgTyA9IGRlZmluZWQodGhpcyk7XG4gICAgICB2YXIgZm4gPSBzZWFyY2hWYWx1ZSA9PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiBzZWFyY2hWYWx1ZVtSRVBMQUNFXTtcbiAgICAgIHJldHVybiBmbiAhPT0gdW5kZWZpbmVkXG4gICAgICAgID8gZm4uY2FsbChzZWFyY2hWYWx1ZSwgTywgcmVwbGFjZVZhbHVlKVxuICAgICAgICA6ICRyZXBsYWNlLmNhbGwoU3RyaW5nKE8pLCBzZWFyY2hWYWx1ZSwgcmVwbGFjZVZhbHVlKTtcbiAgICB9LFxuICAgIC8vIGBSZWdFeHAucHJvdG90eXBlW0BAcmVwbGFjZV1gIG1ldGhvZFxuICAgIC8vIGh0dHBzOi8vdGMzOS5naXRodWIuaW8vZWNtYTI2Mi8jc2VjLXJlZ2V4cC5wcm90b3R5cGUtQEByZXBsYWNlXG4gICAgZnVuY3Rpb24gKHJlZ2V4cCwgcmVwbGFjZVZhbHVlKSB7XG4gICAgICB2YXIgcmVzID0gbWF5YmVDYWxsTmF0aXZlKCRyZXBsYWNlLCByZWdleHAsIHRoaXMsIHJlcGxhY2VWYWx1ZSk7XG4gICAgICBpZiAocmVzLmRvbmUpIHJldHVybiByZXMudmFsdWU7XG5cbiAgICAgIHZhciByeCA9IGFuT2JqZWN0KHJlZ2V4cCk7XG4gICAgICB2YXIgUyA9IFN0cmluZyh0aGlzKTtcbiAgICAgIHZhciBmdW5jdGlvbmFsUmVwbGFjZSA9IHR5cGVvZiByZXBsYWNlVmFsdWUgPT09ICdmdW5jdGlvbic7XG4gICAgICBpZiAoIWZ1bmN0aW9uYWxSZXBsYWNlKSByZXBsYWNlVmFsdWUgPSBTdHJpbmcocmVwbGFjZVZhbHVlKTtcbiAgICAgIHZhciBnbG9iYWwgPSByeC5nbG9iYWw7XG4gICAgICBpZiAoZ2xvYmFsKSB7XG4gICAgICAgIHZhciBmdWxsVW5pY29kZSA9IHJ4LnVuaWNvZGU7XG4gICAgICAgIHJ4Lmxhc3RJbmRleCA9IDA7XG4gICAgICB9XG4gICAgICB2YXIgcmVzdWx0cyA9IFtdO1xuICAgICAgd2hpbGUgKHRydWUpIHtcbiAgICAgICAgdmFyIHJlc3VsdCA9IHJlZ0V4cEV4ZWMocngsIFMpO1xuICAgICAgICBpZiAocmVzdWx0ID09PSBudWxsKSBicmVhaztcbiAgICAgICAgcmVzdWx0cy5wdXNoKHJlc3VsdCk7XG4gICAgICAgIGlmICghZ2xvYmFsKSBicmVhaztcbiAgICAgICAgdmFyIG1hdGNoU3RyID0gU3RyaW5nKHJlc3VsdFswXSk7XG4gICAgICAgIGlmIChtYXRjaFN0ciA9PT0gJycpIHJ4Lmxhc3RJbmRleCA9IGFkdmFuY2VTdHJpbmdJbmRleChTLCB0b0xlbmd0aChyeC5sYXN0SW5kZXgpLCBmdWxsVW5pY29kZSk7XG4gICAgICB9XG4gICAgICB2YXIgYWNjdW11bGF0ZWRSZXN1bHQgPSAnJztcbiAgICAgIHZhciBuZXh0U291cmNlUG9zaXRpb24gPSAwO1xuICAgICAgZm9yICh2YXIgaSA9IDA7IGkgPCByZXN1bHRzLmxlbmd0aDsgaSsrKSB7XG4gICAgICAgIHJlc3VsdCA9IHJlc3VsdHNbaV07XG4gICAgICAgIHZhciBtYXRjaGVkID0gU3RyaW5nKHJlc3VsdFswXSk7XG4gICAgICAgIHZhciBwb3NpdGlvbiA9IG1heChtaW4odG9JbnRlZ2VyKHJlc3VsdC5pbmRleCksIFMubGVuZ3RoKSwgMCk7XG4gICAgICAgIHZhciBjYXB0dXJlcyA9IFtdO1xuICAgICAgICAvLyBOT1RFOiBUaGlzIGlzIGVxdWl2YWxlbnQgdG9cbiAgICAgICAgLy8gICBjYXB0dXJlcyA9IHJlc3VsdC5zbGljZSgxKS5tYXAobWF5YmVUb1N0cmluZylcbiAgICAgICAgLy8gYnV0IGZvciBzb21lIHJlYXNvbiBgbmF0aXZlU2xpY2UuY2FsbChyZXN1bHQsIDEsIHJlc3VsdC5sZW5ndGgpYCAoY2FsbGVkIGluXG4gICAgICAgIC8vIHRoZSBzbGljZSBwb2x5ZmlsbCB3aGVuIHNsaWNpbmcgbmF0aXZlIGFycmF5cykgXCJkb2Vzbid0IHdvcmtcIiBpbiBzYWZhcmkgOSBhbmRcbiAgICAgICAgLy8gY2F1c2VzIGEgY3Jhc2ggKGh0dHBzOi8vcGFzdGViaW4uY29tL04yMVF6ZVFBKSB3aGVuIHRyeWluZyB0byBkZWJ1ZyBpdC5cbiAgICAgICAgZm9yICh2YXIgaiA9IDE7IGogPCByZXN1bHQubGVuZ3RoOyBqKyspIGNhcHR1cmVzLnB1c2gobWF5YmVUb1N0cmluZyhyZXN1bHRbal0pKTtcbiAgICAgICAgdmFyIG5hbWVkQ2FwdHVyZXMgPSByZXN1bHQuZ3JvdXBzO1xuICAgICAgICBpZiAoZnVuY3Rpb25hbFJlcGxhY2UpIHtcbiAgICAgICAgICB2YXIgcmVwbGFjZXJBcmdzID0gW21hdGNoZWRdLmNvbmNhdChjYXB0dXJlcywgcG9zaXRpb24sIFMpO1xuICAgICAgICAgIGlmIChuYW1lZENhcHR1cmVzICE9PSB1bmRlZmluZWQpIHJlcGxhY2VyQXJncy5wdXNoKG5hbWVkQ2FwdHVyZXMpO1xuICAgICAgICAgIHZhciByZXBsYWNlbWVudCA9IFN0cmluZyhyZXBsYWNlVmFsdWUuYXBwbHkodW5kZWZpbmVkLCByZXBsYWNlckFyZ3MpKTtcbiAgICAgICAgfSBlbHNlIHtcbiAgICAgICAgICByZXBsYWNlbWVudCA9IGdldFN1YnN0aXR1dGlvbihtYXRjaGVkLCBTLCBwb3NpdGlvbiwgY2FwdHVyZXMsIG5hbWVkQ2FwdHVyZXMsIHJlcGxhY2VWYWx1ZSk7XG4gICAgICAgIH1cbiAgICAgICAgaWYgKHBvc2l0aW9uID49IG5leHRTb3VyY2VQb3NpdGlvbikge1xuICAgICAgICAgIGFjY3VtdWxhdGVkUmVzdWx0ICs9IFMuc2xpY2UobmV4dFNvdXJjZVBvc2l0aW9uLCBwb3NpdGlvbikgKyByZXBsYWNlbWVudDtcbiAgICAgICAgICBuZXh0U291cmNlUG9zaXRpb24gPSBwb3NpdGlvbiArIG1hdGNoZWQubGVuZ3RoO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICByZXR1cm4gYWNjdW11bGF0ZWRSZXN1bHQgKyBTLnNsaWNlKG5leHRTb3VyY2VQb3NpdGlvbik7XG4gICAgfVxuICBdO1xuXG4gICAgLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9lY21hMjYyLyNzZWMtZ2V0c3Vic3RpdHV0aW9uXG4gIGZ1bmN0aW9uIGdldFN1YnN0aXR1dGlvbihtYXRjaGVkLCBzdHIsIHBvc2l0aW9uLCBjYXB0dXJlcywgbmFtZWRDYXB0dXJlcywgcmVwbGFjZW1lbnQpIHtcbiAgICB2YXIgdGFpbFBvcyA9IHBvc2l0aW9uICsgbWF0Y2hlZC5sZW5ndGg7XG4gICAgdmFyIG0gPSBjYXB0dXJlcy5sZW5ndGg7XG4gICAgdmFyIHN5bWJvbHMgPSBTVUJTVElUVVRJT05fU1lNQk9MU19OT19OQU1FRDtcbiAgICBpZiAobmFtZWRDYXB0dXJlcyAhPT0gdW5kZWZpbmVkKSB7XG4gICAgICBuYW1lZENhcHR1cmVzID0gdG9PYmplY3QobmFtZWRDYXB0dXJlcyk7XG4gICAgICBzeW1ib2xzID0gU1VCU1RJVFVUSU9OX1NZTUJPTFM7XG4gICAgfVxuICAgIHJldHVybiAkcmVwbGFjZS5jYWxsKHJlcGxhY2VtZW50LCBzeW1ib2xzLCBmdW5jdGlvbiAobWF0Y2gsIGNoKSB7XG4gICAgICB2YXIgY2FwdHVyZTtcbiAgICAgIHN3aXRjaCAoY2guY2hhckF0KDApKSB7XG4gICAgICAgIGNhc2UgJyQnOiByZXR1cm4gJyQnO1xuICAgICAgICBjYXNlICcmJzogcmV0dXJuIG1hdGNoZWQ7XG4gICAgICAgIGNhc2UgJ2AnOiByZXR1cm4gc3RyLnNsaWNlKDAsIHBvc2l0aW9uKTtcbiAgICAgICAgY2FzZSBcIidcIjogcmV0dXJuIHN0ci5zbGljZSh0YWlsUG9zKTtcbiAgICAgICAgY2FzZSAnPCc6XG4gICAgICAgICAgY2FwdHVyZSA9IG5hbWVkQ2FwdHVyZXNbY2guc2xpY2UoMSwgLTEpXTtcbiAgICAgICAgICBicmVhaztcbiAgICAgICAgZGVmYXVsdDogLy8gXFxkXFxkP1xuICAgICAgICAgIHZhciBuID0gK2NoO1xuICAgICAgICAgIGlmIChuID09PSAwKSByZXR1cm4gbWF0Y2g7XG4gICAgICAgICAgaWYgKG4gPiBtKSB7XG4gICAgICAgICAgICB2YXIgZiA9IGZsb29yKG4gLyAxMCk7XG4gICAgICAgICAgICBpZiAoZiA9PT0gMCkgcmV0dXJuIG1hdGNoO1xuICAgICAgICAgICAgaWYgKGYgPD0gbSkgcmV0dXJuIGNhcHR1cmVzW2YgLSAxXSA9PT0gdW5kZWZpbmVkID8gY2guY2hhckF0KDEpIDogY2FwdHVyZXNbZiAtIDFdICsgY2guY2hhckF0KDEpO1xuICAgICAgICAgICAgcmV0dXJuIG1hdGNoO1xuICAgICAgICAgIH1cbiAgICAgICAgICBjYXB0dXJlID0gY2FwdHVyZXNbbiAtIDFdO1xuICAgICAgfVxuICAgICAgcmV0dXJuIGNhcHR1cmUgPT09IHVuZGVmaW5lZCA/ICcnIDogY2FwdHVyZTtcbiAgICB9KTtcbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG5cbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIHNhbWVWYWx1ZSA9IHJlcXVpcmUoJy4vX3NhbWUtdmFsdWUnKTtcbnZhciByZWdFeHBFeGVjID0gcmVxdWlyZSgnLi9fcmVnZXhwLWV4ZWMtYWJzdHJhY3QnKTtcblxuLy8gQEBzZWFyY2ggbG9naWNcbnJlcXVpcmUoJy4vX2ZpeC1yZS13a3MnKSgnc2VhcmNoJywgMSwgZnVuY3Rpb24gKGRlZmluZWQsIFNFQVJDSCwgJHNlYXJjaCwgbWF5YmVDYWxsTmF0aXZlKSB7XG4gIHJldHVybiBbXG4gICAgLy8gYFN0cmluZy5wcm90b3R5cGUuc2VhcmNoYCBtZXRob2RcbiAgICAvLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL2VjbWEyNjIvI3NlYy1zdHJpbmcucHJvdG90eXBlLnNlYXJjaFxuICAgIGZ1bmN0aW9uIHNlYXJjaChyZWdleHApIHtcbiAgICAgIHZhciBPID0gZGVmaW5lZCh0aGlzKTtcbiAgICAgIHZhciBmbiA9IHJlZ2V4cCA9PSB1bmRlZmluZWQgPyB1bmRlZmluZWQgOiByZWdleHBbU0VBUkNIXTtcbiAgICAgIHJldHVybiBmbiAhPT0gdW5kZWZpbmVkID8gZm4uY2FsbChyZWdleHAsIE8pIDogbmV3IFJlZ0V4cChyZWdleHApW1NFQVJDSF0oU3RyaW5nKE8pKTtcbiAgICB9LFxuICAgIC8vIGBSZWdFeHAucHJvdG90eXBlW0BAc2VhcmNoXWAgbWV0aG9kXG4gICAgLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9lY21hMjYyLyNzZWMtcmVnZXhwLnByb3RvdHlwZS1AQHNlYXJjaFxuICAgIGZ1bmN0aW9uIChyZWdleHApIHtcbiAgICAgIHZhciByZXMgPSBtYXliZUNhbGxOYXRpdmUoJHNlYXJjaCwgcmVnZXhwLCB0aGlzKTtcbiAgICAgIGlmIChyZXMuZG9uZSkgcmV0dXJuIHJlcy52YWx1ZTtcbiAgICAgIHZhciByeCA9IGFuT2JqZWN0KHJlZ2V4cCk7XG4gICAgICB2YXIgUyA9IFN0cmluZyh0aGlzKTtcbiAgICAgIHZhciBwcmV2aW91c0xhc3RJbmRleCA9IHJ4Lmxhc3RJbmRleDtcbiAgICAgIGlmICghc2FtZVZhbHVlKHByZXZpb3VzTGFzdEluZGV4LCAwKSkgcngubGFzdEluZGV4ID0gMDtcbiAgICAgIHZhciByZXN1bHQgPSByZWdFeHBFeGVjKHJ4LCBTKTtcbiAgICAgIGlmICghc2FtZVZhbHVlKHJ4Lmxhc3RJbmRleCwgcHJldmlvdXNMYXN0SW5kZXgpKSByeC5sYXN0SW5kZXggPSBwcmV2aW91c0xhc3RJbmRleDtcbiAgICAgIHJldHVybiByZXN1bHQgPT09IG51bGwgPyAtMSA6IHJlc3VsdC5pbmRleDtcbiAgICB9XG4gIF07XG59KTtcbiIsIid1c2Ugc3RyaWN0JztcblxudmFyIGlzUmVnRXhwID0gcmVxdWlyZSgnLi9faXMtcmVnZXhwJyk7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciBzcGVjaWVzQ29uc3RydWN0b3IgPSByZXF1aXJlKCcuL19zcGVjaWVzLWNvbnN0cnVjdG9yJyk7XG52YXIgYWR2YW5jZVN0cmluZ0luZGV4ID0gcmVxdWlyZSgnLi9fYWR2YW5jZS1zdHJpbmctaW5kZXgnKTtcbnZhciB0b0xlbmd0aCA9IHJlcXVpcmUoJy4vX3RvLWxlbmd0aCcpO1xudmFyIGNhbGxSZWdFeHBFeGVjID0gcmVxdWlyZSgnLi9fcmVnZXhwLWV4ZWMtYWJzdHJhY3QnKTtcbnZhciByZWdleHBFeGVjID0gcmVxdWlyZSgnLi9fcmVnZXhwLWV4ZWMnKTtcbnZhciBmYWlscyA9IHJlcXVpcmUoJy4vX2ZhaWxzJyk7XG52YXIgJG1pbiA9IE1hdGgubWluO1xudmFyICRwdXNoID0gW10ucHVzaDtcbnZhciAkU1BMSVQgPSAnc3BsaXQnO1xudmFyIExFTkdUSCA9ICdsZW5ndGgnO1xudmFyIExBU1RfSU5ERVggPSAnbGFzdEluZGV4JztcbnZhciBNQVhfVUlOVDMyID0gMHhmZmZmZmZmZjtcblxuLy8gYmFiZWwtbWluaWZ5IHRyYW5zcGlsZXMgUmVnRXhwKCd4JywgJ3knKSAtPiAveC95IGFuZCBpdCBjYXVzZXMgU3ludGF4RXJyb3JcbnZhciBTVVBQT1JUU19ZID0gIWZhaWxzKGZ1bmN0aW9uICgpIHsgUmVnRXhwKE1BWF9VSU5UMzIsICd5Jyk7IH0pO1xuXG4vLyBAQHNwbGl0IGxvZ2ljXG5yZXF1aXJlKCcuL19maXgtcmUtd2tzJykoJ3NwbGl0JywgMiwgZnVuY3Rpb24gKGRlZmluZWQsIFNQTElULCAkc3BsaXQsIG1heWJlQ2FsbE5hdGl2ZSkge1xuICB2YXIgaW50ZXJuYWxTcGxpdDtcbiAgaWYgKFxuICAgICdhYmJjJ1skU1BMSVRdKC8oYikqLylbMV0gPT0gJ2MnIHx8XG4gICAgJ3Rlc3QnWyRTUExJVF0oLyg/OikvLCAtMSlbTEVOR1RIXSAhPSA0IHx8XG4gICAgJ2FiJ1skU1BMSVRdKC8oPzphYikqLylbTEVOR1RIXSAhPSAyIHx8XG4gICAgJy4nWyRTUExJVF0oLyguPykoLj8pLylbTEVOR1RIXSAhPSA0IHx8XG4gICAgJy4nWyRTUExJVF0oLygpKCkvKVtMRU5HVEhdID4gMSB8fFxuICAgICcnWyRTUExJVF0oLy4/LylbTEVOR1RIXVxuICApIHtcbiAgICAvLyBiYXNlZCBvbiBlczUtc2hpbSBpbXBsZW1lbnRhdGlvbiwgbmVlZCB0byByZXdvcmsgaXRcbiAgICBpbnRlcm5hbFNwbGl0ID0gZnVuY3Rpb24gKHNlcGFyYXRvciwgbGltaXQpIHtcbiAgICAgIHZhciBzdHJpbmcgPSBTdHJpbmcodGhpcyk7XG4gICAgICBpZiAoc2VwYXJhdG9yID09PSB1bmRlZmluZWQgJiYgbGltaXQgPT09IDApIHJldHVybiBbXTtcbiAgICAgIC8vIElmIGBzZXBhcmF0b3JgIGlzIG5vdCBhIHJlZ2V4LCB1c2UgbmF0aXZlIHNwbGl0XG4gICAgICBpZiAoIWlzUmVnRXhwKHNlcGFyYXRvcikpIHJldHVybiAkc3BsaXQuY2FsbChzdHJpbmcsIHNlcGFyYXRvciwgbGltaXQpO1xuICAgICAgdmFyIG91dHB1dCA9IFtdO1xuICAgICAgdmFyIGZsYWdzID0gKHNlcGFyYXRvci5pZ25vcmVDYXNlID8gJ2knIDogJycpICtcbiAgICAgICAgICAgICAgICAgIChzZXBhcmF0b3IubXVsdGlsaW5lID8gJ20nIDogJycpICtcbiAgICAgICAgICAgICAgICAgIChzZXBhcmF0b3IudW5pY29kZSA/ICd1JyA6ICcnKSArXG4gICAgICAgICAgICAgICAgICAoc2VwYXJhdG9yLnN0aWNreSA/ICd5JyA6ICcnKTtcbiAgICAgIHZhciBsYXN0TGFzdEluZGV4ID0gMDtcbiAgICAgIHZhciBzcGxpdExpbWl0ID0gbGltaXQgPT09IHVuZGVmaW5lZCA/IE1BWF9VSU5UMzIgOiBsaW1pdCA+Pj4gMDtcbiAgICAgIC8vIE1ha2UgYGdsb2JhbGAgYW5kIGF2b2lkIGBsYXN0SW5kZXhgIGlzc3VlcyBieSB3b3JraW5nIHdpdGggYSBjb3B5XG4gICAgICB2YXIgc2VwYXJhdG9yQ29weSA9IG5ldyBSZWdFeHAoc2VwYXJhdG9yLnNvdXJjZSwgZmxhZ3MgKyAnZycpO1xuICAgICAgdmFyIG1hdGNoLCBsYXN0SW5kZXgsIGxhc3RMZW5ndGg7XG4gICAgICB3aGlsZSAobWF0Y2ggPSByZWdleHBFeGVjLmNhbGwoc2VwYXJhdG9yQ29weSwgc3RyaW5nKSkge1xuICAgICAgICBsYXN0SW5kZXggPSBzZXBhcmF0b3JDb3B5W0xBU1RfSU5ERVhdO1xuICAgICAgICBpZiAobGFzdEluZGV4ID4gbGFzdExhc3RJbmRleCkge1xuICAgICAgICAgIG91dHB1dC5wdXNoKHN0cmluZy5zbGljZShsYXN0TGFzdEluZGV4LCBtYXRjaC5pbmRleCkpO1xuICAgICAgICAgIGlmIChtYXRjaFtMRU5HVEhdID4gMSAmJiBtYXRjaC5pbmRleCA8IHN0cmluZ1tMRU5HVEhdKSAkcHVzaC5hcHBseShvdXRwdXQsIG1hdGNoLnNsaWNlKDEpKTtcbiAgICAgICAgICBsYXN0TGVuZ3RoID0gbWF0Y2hbMF1bTEVOR1RIXTtcbiAgICAgICAgICBsYXN0TGFzdEluZGV4ID0gbGFzdEluZGV4O1xuICAgICAgICAgIGlmIChvdXRwdXRbTEVOR1RIXSA+PSBzcGxpdExpbWl0KSBicmVhaztcbiAgICAgICAgfVxuICAgICAgICBpZiAoc2VwYXJhdG9yQ29weVtMQVNUX0lOREVYXSA9PT0gbWF0Y2guaW5kZXgpIHNlcGFyYXRvckNvcHlbTEFTVF9JTkRFWF0rKzsgLy8gQXZvaWQgYW4gaW5maW5pdGUgbG9vcFxuICAgICAgfVxuICAgICAgaWYgKGxhc3RMYXN0SW5kZXggPT09IHN0cmluZ1tMRU5HVEhdKSB7XG4gICAgICAgIGlmIChsYXN0TGVuZ3RoIHx8ICFzZXBhcmF0b3JDb3B5LnRlc3QoJycpKSBvdXRwdXQucHVzaCgnJyk7XG4gICAgICB9IGVsc2Ugb3V0cHV0LnB1c2goc3RyaW5nLnNsaWNlKGxhc3RMYXN0SW5kZXgpKTtcbiAgICAgIHJldHVybiBvdXRwdXRbTEVOR1RIXSA+IHNwbGl0TGltaXQgPyBvdXRwdXQuc2xpY2UoMCwgc3BsaXRMaW1pdCkgOiBvdXRwdXQ7XG4gICAgfTtcbiAgLy8gQ2hha3JhLCBWOFxuICB9IGVsc2UgaWYgKCcwJ1skU1BMSVRdKHVuZGVmaW5lZCwgMClbTEVOR1RIXSkge1xuICAgIGludGVybmFsU3BsaXQgPSBmdW5jdGlvbiAoc2VwYXJhdG9yLCBsaW1pdCkge1xuICAgICAgcmV0dXJuIHNlcGFyYXRvciA9PT0gdW5kZWZpbmVkICYmIGxpbWl0ID09PSAwID8gW10gOiAkc3BsaXQuY2FsbCh0aGlzLCBzZXBhcmF0b3IsIGxpbWl0KTtcbiAgICB9O1xuICB9IGVsc2Uge1xuICAgIGludGVybmFsU3BsaXQgPSAkc3BsaXQ7XG4gIH1cblxuICByZXR1cm4gW1xuICAgIC8vIGBTdHJpbmcucHJvdG90eXBlLnNwbGl0YCBtZXRob2RcbiAgICAvLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL2VjbWEyNjIvI3NlYy1zdHJpbmcucHJvdG90eXBlLnNwbGl0XG4gICAgZnVuY3Rpb24gc3BsaXQoc2VwYXJhdG9yLCBsaW1pdCkge1xuICAgICAgdmFyIE8gPSBkZWZpbmVkKHRoaXMpO1xuICAgICAgdmFyIHNwbGl0dGVyID0gc2VwYXJhdG9yID09IHVuZGVmaW5lZCA/IHVuZGVmaW5lZCA6IHNlcGFyYXRvcltTUExJVF07XG4gICAgICByZXR1cm4gc3BsaXR0ZXIgIT09IHVuZGVmaW5lZFxuICAgICAgICA/IHNwbGl0dGVyLmNhbGwoc2VwYXJhdG9yLCBPLCBsaW1pdClcbiAgICAgICAgOiBpbnRlcm5hbFNwbGl0LmNhbGwoU3RyaW5nKE8pLCBzZXBhcmF0b3IsIGxpbWl0KTtcbiAgICB9LFxuICAgIC8vIGBSZWdFeHAucHJvdG90eXBlW0BAc3BsaXRdYCBtZXRob2RcbiAgICAvLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL2VjbWEyNjIvI3NlYy1yZWdleHAucHJvdG90eXBlLUBAc3BsaXRcbiAgICAvL1xuICAgIC8vIE5PVEU6IFRoaXMgY2Fubm90IGJlIHByb3Blcmx5IHBvbHlmaWxsZWQgaW4gZW5naW5lcyB0aGF0IGRvbid0IHN1cHBvcnRcbiAgICAvLyB0aGUgJ3knIGZsYWcuXG4gICAgZnVuY3Rpb24gKHJlZ2V4cCwgbGltaXQpIHtcbiAgICAgIHZhciByZXMgPSBtYXliZUNhbGxOYXRpdmUoaW50ZXJuYWxTcGxpdCwgcmVnZXhwLCB0aGlzLCBsaW1pdCwgaW50ZXJuYWxTcGxpdCAhPT0gJHNwbGl0KTtcbiAgICAgIGlmIChyZXMuZG9uZSkgcmV0dXJuIHJlcy52YWx1ZTtcblxuICAgICAgdmFyIHJ4ID0gYW5PYmplY3QocmVnZXhwKTtcbiAgICAgIHZhciBTID0gU3RyaW5nKHRoaXMpO1xuICAgICAgdmFyIEMgPSBzcGVjaWVzQ29uc3RydWN0b3IocngsIFJlZ0V4cCk7XG5cbiAgICAgIHZhciB1bmljb2RlTWF0Y2hpbmcgPSByeC51bmljb2RlO1xuICAgICAgdmFyIGZsYWdzID0gKHJ4Lmlnbm9yZUNhc2UgPyAnaScgOiAnJykgK1xuICAgICAgICAgICAgICAgICAgKHJ4Lm11bHRpbGluZSA/ICdtJyA6ICcnKSArXG4gICAgICAgICAgICAgICAgICAocngudW5pY29kZSA/ICd1JyA6ICcnKSArXG4gICAgICAgICAgICAgICAgICAoU1VQUE9SVFNfWSA/ICd5JyA6ICdnJyk7XG5cbiAgICAgIC8vIF4oPyArIHJ4ICsgKSBpcyBuZWVkZWQsIGluIGNvbWJpbmF0aW9uIHdpdGggc29tZSBTIHNsaWNpbmcsIHRvXG4gICAgICAvLyBzaW11bGF0ZSB0aGUgJ3knIGZsYWcuXG4gICAgICB2YXIgc3BsaXR0ZXIgPSBuZXcgQyhTVVBQT1JUU19ZID8gcnggOiAnXig/OicgKyByeC5zb3VyY2UgKyAnKScsIGZsYWdzKTtcbiAgICAgIHZhciBsaW0gPSBsaW1pdCA9PT0gdW5kZWZpbmVkID8gTUFYX1VJTlQzMiA6IGxpbWl0ID4+PiAwO1xuICAgICAgaWYgKGxpbSA9PT0gMCkgcmV0dXJuIFtdO1xuICAgICAgaWYgKFMubGVuZ3RoID09PSAwKSByZXR1cm4gY2FsbFJlZ0V4cEV4ZWMoc3BsaXR0ZXIsIFMpID09PSBudWxsID8gW1NdIDogW107XG4gICAgICB2YXIgcCA9IDA7XG4gICAgICB2YXIgcSA9IDA7XG4gICAgICB2YXIgQSA9IFtdO1xuICAgICAgd2hpbGUgKHEgPCBTLmxlbmd0aCkge1xuICAgICAgICBzcGxpdHRlci5sYXN0SW5kZXggPSBTVVBQT1JUU19ZID8gcSA6IDA7XG4gICAgICAgIHZhciB6ID0gY2FsbFJlZ0V4cEV4ZWMoc3BsaXR0ZXIsIFNVUFBPUlRTX1kgPyBTIDogUy5zbGljZShxKSk7XG4gICAgICAgIHZhciBlO1xuICAgICAgICBpZiAoXG4gICAgICAgICAgeiA9PT0gbnVsbCB8fFxuICAgICAgICAgIChlID0gJG1pbih0b0xlbmd0aChzcGxpdHRlci5sYXN0SW5kZXggKyAoU1VQUE9SVFNfWSA/IDAgOiBxKSksIFMubGVuZ3RoKSkgPT09IHBcbiAgICAgICAgKSB7XG4gICAgICAgICAgcSA9IGFkdmFuY2VTdHJpbmdJbmRleChTLCBxLCB1bmljb2RlTWF0Y2hpbmcpO1xuICAgICAgICB9IGVsc2Uge1xuICAgICAgICAgIEEucHVzaChTLnNsaWNlKHAsIHEpKTtcbiAgICAgICAgICBpZiAoQS5sZW5ndGggPT09IGxpbSkgcmV0dXJuIEE7XG4gICAgICAgICAgZm9yICh2YXIgaSA9IDE7IGkgPD0gei5sZW5ndGggLSAxOyBpKyspIHtcbiAgICAgICAgICAgIEEucHVzaCh6W2ldKTtcbiAgICAgICAgICAgIGlmIChBLmxlbmd0aCA9PT0gbGltKSByZXR1cm4gQTtcbiAgICAgICAgICB9XG4gICAgICAgICAgcSA9IHAgPSBlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgICBBLnB1c2goUy5zbGljZShwKSk7XG4gICAgICByZXR1cm4gQTtcbiAgICB9XG4gIF07XG59KTtcbiIsIid1c2Ugc3RyaWN0JztcbnJlcXVpcmUoJy4vZXM2LnJlZ2V4cC5mbGFncycpO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgJGZsYWdzID0gcmVxdWlyZSgnLi9fZmxhZ3MnKTtcbnZhciBERVNDUklQVE9SUyA9IHJlcXVpcmUoJy4vX2Rlc2NyaXB0b3JzJyk7XG52YXIgVE9fU1RSSU5HID0gJ3RvU3RyaW5nJztcbnZhciAkdG9TdHJpbmcgPSAvLi9bVE9fU1RSSU5HXTtcblxudmFyIGRlZmluZSA9IGZ1bmN0aW9uIChmbikge1xuICByZXF1aXJlKCcuL19yZWRlZmluZScpKFJlZ0V4cC5wcm90b3R5cGUsIFRPX1NUUklORywgZm4sIHRydWUpO1xufTtcblxuLy8gMjEuMi41LjE0IFJlZ0V4cC5wcm90b3R5cGUudG9TdHJpbmcoKVxuaWYgKHJlcXVpcmUoJy4vX2ZhaWxzJykoZnVuY3Rpb24gKCkgeyByZXR1cm4gJHRvU3RyaW5nLmNhbGwoeyBzb3VyY2U6ICdhJywgZmxhZ3M6ICdiJyB9KSAhPSAnL2EvYic7IH0pKSB7XG4gIGRlZmluZShmdW5jdGlvbiB0b1N0cmluZygpIHtcbiAgICB2YXIgUiA9IGFuT2JqZWN0KHRoaXMpO1xuICAgIHJldHVybiAnLycuY29uY2F0KFIuc291cmNlLCAnLycsXG4gICAgICAnZmxhZ3MnIGluIFIgPyBSLmZsYWdzIDogIURFU0NSSVBUT1JTICYmIFIgaW5zdGFuY2VvZiBSZWdFeHAgPyAkZmxhZ3MuY2FsbChSKSA6IHVuZGVmaW5lZCk7XG4gIH0pO1xuLy8gRkY0NC0gUmVnRXhwI3RvU3RyaW5nIGhhcyBhIHdyb25nIG5hbWVcbn0gZWxzZSBpZiAoJHRvU3RyaW5nLm5hbWUgIT0gVE9fU1RSSU5HKSB7XG4gIGRlZmluZShmdW5jdGlvbiB0b1N0cmluZygpIHtcbiAgICByZXR1cm4gJHRvU3RyaW5nLmNhbGwodGhpcyk7XG4gIH0pO1xufVxuIiwiJ3VzZSBzdHJpY3QnO1xudmFyIHN0cm9uZyA9IHJlcXVpcmUoJy4vX2NvbGxlY3Rpb24tc3Ryb25nJyk7XG52YXIgdmFsaWRhdGUgPSByZXF1aXJlKCcuL192YWxpZGF0ZS1jb2xsZWN0aW9uJyk7XG52YXIgU0VUID0gJ1NldCc7XG5cbi8vIDIzLjIgU2V0IE9iamVjdHNcbm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZSgnLi9fY29sbGVjdGlvbicpKFNFVCwgZnVuY3Rpb24gKGdldCkge1xuICByZXR1cm4gZnVuY3Rpb24gU2V0KCkgeyByZXR1cm4gZ2V0KHRoaXMsIGFyZ3VtZW50cy5sZW5ndGggPiAwID8gYXJndW1lbnRzWzBdIDogdW5kZWZpbmVkKTsgfTtcbn0sIHtcbiAgLy8gMjMuMi4zLjEgU2V0LnByb3RvdHlwZS5hZGQodmFsdWUpXG4gIGFkZDogZnVuY3Rpb24gYWRkKHZhbHVlKSB7XG4gICAgcmV0dXJuIHN0cm9uZy5kZWYodmFsaWRhdGUodGhpcywgU0VUKSwgdmFsdWUgPSB2YWx1ZSA9PT0gMCA/IDAgOiB2YWx1ZSwgdmFsdWUpO1xuICB9XG59LCBzdHJvbmcpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gQi4yLjMuMiBTdHJpbmcucHJvdG90eXBlLmFuY2hvcihuYW1lKVxucmVxdWlyZSgnLi9fc3RyaW5nLWh0bWwnKSgnYW5jaG9yJywgZnVuY3Rpb24gKGNyZWF0ZUhUTUwpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIGFuY2hvcihuYW1lKSB7XG4gICAgcmV0dXJuIGNyZWF0ZUhUTUwodGhpcywgJ2EnLCAnbmFtZScsIG5hbWUpO1xuICB9O1xufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBCLjIuMy4zIFN0cmluZy5wcm90b3R5cGUuYmlnKClcbnJlcXVpcmUoJy4vX3N0cmluZy1odG1sJykoJ2JpZycsIGZ1bmN0aW9uIChjcmVhdGVIVE1MKSB7XG4gIHJldHVybiBmdW5jdGlvbiBiaWcoKSB7XG4gICAgcmV0dXJuIGNyZWF0ZUhUTUwodGhpcywgJ2JpZycsICcnLCAnJyk7XG4gIH07XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIEIuMi4zLjQgU3RyaW5nLnByb3RvdHlwZS5ibGluaygpXG5yZXF1aXJlKCcuL19zdHJpbmctaHRtbCcpKCdibGluaycsIGZ1bmN0aW9uIChjcmVhdGVIVE1MKSB7XG4gIHJldHVybiBmdW5jdGlvbiBibGluaygpIHtcbiAgICByZXR1cm4gY3JlYXRlSFRNTCh0aGlzLCAnYmxpbmsnLCAnJywgJycpO1xuICB9O1xufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBCLjIuMy41IFN0cmluZy5wcm90b3R5cGUuYm9sZCgpXG5yZXF1aXJlKCcuL19zdHJpbmctaHRtbCcpKCdib2xkJywgZnVuY3Rpb24gKGNyZWF0ZUhUTUwpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIGJvbGQoKSB7XG4gICAgcmV0dXJuIGNyZWF0ZUhUTUwodGhpcywgJ2InLCAnJywgJycpO1xuICB9O1xufSk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRhdCA9IHJlcXVpcmUoJy4vX3N0cmluZy1hdCcpKGZhbHNlKTtcbiRleHBvcnQoJGV4cG9ydC5QLCAnU3RyaW5nJywge1xuICAvLyAyMS4xLjMuMyBTdHJpbmcucHJvdG90eXBlLmNvZGVQb2ludEF0KHBvcylcbiAgY29kZVBvaW50QXQ6IGZ1bmN0aW9uIGNvZGVQb2ludEF0KHBvcykge1xuICAgIHJldHVybiAkYXQodGhpcywgcG9zKTtcbiAgfVxufSk7XG4iLCIvLyAyMS4xLjMuNiBTdHJpbmcucHJvdG90eXBlLmVuZHNXaXRoKHNlYXJjaFN0cmluZyBbLCBlbmRQb3NpdGlvbl0pXG4ndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG52YXIgY29udGV4dCA9IHJlcXVpcmUoJy4vX3N0cmluZy1jb250ZXh0Jyk7XG52YXIgRU5EU19XSVRIID0gJ2VuZHNXaXRoJztcbnZhciAkZW5kc1dpdGggPSAnJ1tFTkRTX1dJVEhdO1xuXG4kZXhwb3J0KCRleHBvcnQuUCArICRleHBvcnQuRiAqIHJlcXVpcmUoJy4vX2ZhaWxzLWlzLXJlZ2V4cCcpKEVORFNfV0lUSCksICdTdHJpbmcnLCB7XG4gIGVuZHNXaXRoOiBmdW5jdGlvbiBlbmRzV2l0aChzZWFyY2hTdHJpbmcgLyogLCBlbmRQb3NpdGlvbiA9IEBsZW5ndGggKi8pIHtcbiAgICB2YXIgdGhhdCA9IGNvbnRleHQodGhpcywgc2VhcmNoU3RyaW5nLCBFTkRTX1dJVEgpO1xuICAgIHZhciBlbmRQb3NpdGlvbiA9IGFyZ3VtZW50cy5sZW5ndGggPiAxID8gYXJndW1lbnRzWzFdIDogdW5kZWZpbmVkO1xuICAgIHZhciBsZW4gPSB0b0xlbmd0aCh0aGF0Lmxlbmd0aCk7XG4gICAgdmFyIGVuZCA9IGVuZFBvc2l0aW9uID09PSB1bmRlZmluZWQgPyBsZW4gOiBNYXRoLm1pbih0b0xlbmd0aChlbmRQb3NpdGlvbiksIGxlbik7XG4gICAgdmFyIHNlYXJjaCA9IFN0cmluZyhzZWFyY2hTdHJpbmcpO1xuICAgIHJldHVybiAkZW5kc1dpdGhcbiAgICAgID8gJGVuZHNXaXRoLmNhbGwodGhhdCwgc2VhcmNoLCBlbmQpXG4gICAgICA6IHRoYXQuc2xpY2UoZW5kIC0gc2VhcmNoLmxlbmd0aCwgZW5kKSA9PT0gc2VhcmNoO1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIEIuMi4zLjYgU3RyaW5nLnByb3RvdHlwZS5maXhlZCgpXG5yZXF1aXJlKCcuL19zdHJpbmctaHRtbCcpKCdmaXhlZCcsIGZ1bmN0aW9uIChjcmVhdGVIVE1MKSB7XG4gIHJldHVybiBmdW5jdGlvbiBmaXhlZCgpIHtcbiAgICByZXR1cm4gY3JlYXRlSFRNTCh0aGlzLCAndHQnLCAnJywgJycpO1xuICB9O1xufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBCLjIuMy43IFN0cmluZy5wcm90b3R5cGUuZm9udGNvbG9yKGNvbG9yKVxucmVxdWlyZSgnLi9fc3RyaW5nLWh0bWwnKSgnZm9udGNvbG9yJywgZnVuY3Rpb24gKGNyZWF0ZUhUTUwpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIGZvbnRjb2xvcihjb2xvcikge1xuICAgIHJldHVybiBjcmVhdGVIVE1MKHRoaXMsICdmb250JywgJ2NvbG9yJywgY29sb3IpO1xuICB9O1xufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBCLjIuMy44IFN0cmluZy5wcm90b3R5cGUuZm9udHNpemUoc2l6ZSlcbnJlcXVpcmUoJy4vX3N0cmluZy1odG1sJykoJ2ZvbnRzaXplJywgZnVuY3Rpb24gKGNyZWF0ZUhUTUwpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIGZvbnRzaXplKHNpemUpIHtcbiAgICByZXR1cm4gY3JlYXRlSFRNTCh0aGlzLCAnZm9udCcsICdzaXplJywgc2l6ZSk7XG4gIH07XG59KTtcbiIsInZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgdG9BYnNvbHV0ZUluZGV4ID0gcmVxdWlyZSgnLi9fdG8tYWJzb2x1dGUtaW5kZXgnKTtcbnZhciBmcm9tQ2hhckNvZGUgPSBTdHJpbmcuZnJvbUNoYXJDb2RlO1xudmFyICRmcm9tQ29kZVBvaW50ID0gU3RyaW5nLmZyb21Db2RlUG9pbnQ7XG5cbi8vIGxlbmd0aCBzaG91bGQgYmUgMSwgb2xkIEZGIHByb2JsZW1cbiRleHBvcnQoJGV4cG9ydC5TICsgJGV4cG9ydC5GICogKCEhJGZyb21Db2RlUG9pbnQgJiYgJGZyb21Db2RlUG9pbnQubGVuZ3RoICE9IDEpLCAnU3RyaW5nJywge1xuICAvLyAyMS4xLjIuMiBTdHJpbmcuZnJvbUNvZGVQb2ludCguLi5jb2RlUG9pbnRzKVxuICBmcm9tQ29kZVBvaW50OiBmdW5jdGlvbiBmcm9tQ29kZVBvaW50KHgpIHsgLy8gZXNsaW50LWRpc2FibGUtbGluZSBuby11bnVzZWQtdmFyc1xuICAgIHZhciByZXMgPSBbXTtcbiAgICB2YXIgYUxlbiA9IGFyZ3VtZW50cy5sZW5ndGg7XG4gICAgdmFyIGkgPSAwO1xuICAgIHZhciBjb2RlO1xuICAgIHdoaWxlIChhTGVuID4gaSkge1xuICAgICAgY29kZSA9ICthcmd1bWVudHNbaSsrXTtcbiAgICAgIGlmICh0b0Fic29sdXRlSW5kZXgoY29kZSwgMHgxMGZmZmYpICE9PSBjb2RlKSB0aHJvdyBSYW5nZUVycm9yKGNvZGUgKyAnIGlzIG5vdCBhIHZhbGlkIGNvZGUgcG9pbnQnKTtcbiAgICAgIHJlcy5wdXNoKGNvZGUgPCAweDEwMDAwXG4gICAgICAgID8gZnJvbUNoYXJDb2RlKGNvZGUpXG4gICAgICAgIDogZnJvbUNoYXJDb2RlKCgoY29kZSAtPSAweDEwMDAwKSA+PiAxMCkgKyAweGQ4MDAsIGNvZGUgJSAweDQwMCArIDB4ZGMwMClcbiAgICAgICk7XG4gICAgfSByZXR1cm4gcmVzLmpvaW4oJycpO1xuICB9XG59KTtcbiIsIi8vIDIxLjEuMy43IFN0cmluZy5wcm90b3R5cGUuaW5jbHVkZXMoc2VhcmNoU3RyaW5nLCBwb3NpdGlvbiA9IDApXG4ndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGNvbnRleHQgPSByZXF1aXJlKCcuL19zdHJpbmctY29udGV4dCcpO1xudmFyIElOQ0xVREVTID0gJ2luY2x1ZGVzJztcblxuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiByZXF1aXJlKCcuL19mYWlscy1pcy1yZWdleHAnKShJTkNMVURFUyksICdTdHJpbmcnLCB7XG4gIGluY2x1ZGVzOiBmdW5jdGlvbiBpbmNsdWRlcyhzZWFyY2hTdHJpbmcgLyogLCBwb3NpdGlvbiA9IDAgKi8pIHtcbiAgICByZXR1cm4gISF+Y29udGV4dCh0aGlzLCBzZWFyY2hTdHJpbmcsIElOQ0xVREVTKVxuICAgICAgLmluZGV4T2Yoc2VhcmNoU3RyaW5nLCBhcmd1bWVudHMubGVuZ3RoID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCk7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gQi4yLjMuOSBTdHJpbmcucHJvdG90eXBlLml0YWxpY3MoKVxucmVxdWlyZSgnLi9fc3RyaW5nLWh0bWwnKSgnaXRhbGljcycsIGZ1bmN0aW9uIChjcmVhdGVIVE1MKSB7XG4gIHJldHVybiBmdW5jdGlvbiBpdGFsaWNzKCkge1xuICAgIHJldHVybiBjcmVhdGVIVE1MKHRoaXMsICdpJywgJycsICcnKTtcbiAgfTtcbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyICRhdCA9IHJlcXVpcmUoJy4vX3N0cmluZy1hdCcpKHRydWUpO1xuXG4vLyAyMS4xLjMuMjcgU3RyaW5nLnByb3RvdHlwZVtAQGl0ZXJhdG9yXSgpXG5yZXF1aXJlKCcuL19pdGVyLWRlZmluZScpKFN0cmluZywgJ1N0cmluZycsIGZ1bmN0aW9uIChpdGVyYXRlZCkge1xuICB0aGlzLl90ID0gU3RyaW5nKGl0ZXJhdGVkKTsgLy8gdGFyZ2V0XG4gIHRoaXMuX2kgPSAwOyAgICAgICAgICAgICAgICAvLyBuZXh0IGluZGV4XG4vLyAyMS4xLjUuMi4xICVTdHJpbmdJdGVyYXRvclByb3RvdHlwZSUubmV4dCgpXG59LCBmdW5jdGlvbiAoKSB7XG4gIHZhciBPID0gdGhpcy5fdDtcbiAgdmFyIGluZGV4ID0gdGhpcy5faTtcbiAgdmFyIHBvaW50O1xuICBpZiAoaW5kZXggPj0gTy5sZW5ndGgpIHJldHVybiB7IHZhbHVlOiB1bmRlZmluZWQsIGRvbmU6IHRydWUgfTtcbiAgcG9pbnQgPSAkYXQoTywgaW5kZXgpO1xuICB0aGlzLl9pICs9IHBvaW50Lmxlbmd0aDtcbiAgcmV0dXJuIHsgdmFsdWU6IHBvaW50LCBkb25lOiBmYWxzZSB9O1xufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBCLjIuMy4xMCBTdHJpbmcucHJvdG90eXBlLmxpbmsodXJsKVxucmVxdWlyZSgnLi9fc3RyaW5nLWh0bWwnKSgnbGluaycsIGZ1bmN0aW9uIChjcmVhdGVIVE1MKSB7XG4gIHJldHVybiBmdW5jdGlvbiBsaW5rKHVybCkge1xuICAgIHJldHVybiBjcmVhdGVIVE1MKHRoaXMsICdhJywgJ2hyZWYnLCB1cmwpO1xuICB9O1xufSk7XG4iLCJ2YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIHRvSU9iamVjdCA9IHJlcXVpcmUoJy4vX3RvLWlvYmplY3QnKTtcbnZhciB0b0xlbmd0aCA9IHJlcXVpcmUoJy4vX3RvLWxlbmd0aCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ1N0cmluZycsIHtcbiAgLy8gMjEuMS4yLjQgU3RyaW5nLnJhdyhjYWxsU2l0ZSwgLi4uc3Vic3RpdHV0aW9ucylcbiAgcmF3OiBmdW5jdGlvbiByYXcoY2FsbFNpdGUpIHtcbiAgICB2YXIgdHBsID0gdG9JT2JqZWN0KGNhbGxTaXRlLnJhdyk7XG4gICAgdmFyIGxlbiA9IHRvTGVuZ3RoKHRwbC5sZW5ndGgpO1xuICAgIHZhciBhTGVuID0gYXJndW1lbnRzLmxlbmd0aDtcbiAgICB2YXIgcmVzID0gW107XG4gICAgdmFyIGkgPSAwO1xuICAgIHdoaWxlIChsZW4gPiBpKSB7XG4gICAgICByZXMucHVzaChTdHJpbmcodHBsW2krK10pKTtcbiAgICAgIGlmIChpIDwgYUxlbikgcmVzLnB1c2goU3RyaW5nKGFyZ3VtZW50c1tpXSkpO1xuICAgIH0gcmV0dXJuIHJlcy5qb2luKCcnKTtcbiAgfVxufSk7XG4iLCJ2YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCwgJ1N0cmluZycsIHtcbiAgLy8gMjEuMS4zLjEzIFN0cmluZy5wcm90b3R5cGUucmVwZWF0KGNvdW50KVxuICByZXBlYXQ6IHJlcXVpcmUoJy4vX3N0cmluZy1yZXBlYXQnKVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBCLjIuMy4xMSBTdHJpbmcucHJvdG90eXBlLnNtYWxsKClcbnJlcXVpcmUoJy4vX3N0cmluZy1odG1sJykoJ3NtYWxsJywgZnVuY3Rpb24gKGNyZWF0ZUhUTUwpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIHNtYWxsKCkge1xuICAgIHJldHVybiBjcmVhdGVIVE1MKHRoaXMsICdzbWFsbCcsICcnLCAnJyk7XG4gIH07XG59KTtcbiIsIi8vIDIxLjEuMy4xOCBTdHJpbmcucHJvdG90eXBlLnN0YXJ0c1dpdGgoc2VhcmNoU3RyaW5nIFssIHBvc2l0aW9uIF0pXG4ndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG52YXIgY29udGV4dCA9IHJlcXVpcmUoJy4vX3N0cmluZy1jb250ZXh0Jyk7XG52YXIgU1RBUlRTX1dJVEggPSAnc3RhcnRzV2l0aCc7XG52YXIgJHN0YXJ0c1dpdGggPSAnJ1tTVEFSVFNfV0lUSF07XG5cbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogcmVxdWlyZSgnLi9fZmFpbHMtaXMtcmVnZXhwJykoU1RBUlRTX1dJVEgpLCAnU3RyaW5nJywge1xuICBzdGFydHNXaXRoOiBmdW5jdGlvbiBzdGFydHNXaXRoKHNlYXJjaFN0cmluZyAvKiAsIHBvc2l0aW9uID0gMCAqLykge1xuICAgIHZhciB0aGF0ID0gY29udGV4dCh0aGlzLCBzZWFyY2hTdHJpbmcsIFNUQVJUU19XSVRIKTtcbiAgICB2YXIgaW5kZXggPSB0b0xlbmd0aChNYXRoLm1pbihhcmd1bWVudHMubGVuZ3RoID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCwgdGhhdC5sZW5ndGgpKTtcbiAgICB2YXIgc2VhcmNoID0gU3RyaW5nKHNlYXJjaFN0cmluZyk7XG4gICAgcmV0dXJuICRzdGFydHNXaXRoXG4gICAgICA/ICRzdGFydHNXaXRoLmNhbGwodGhhdCwgc2VhcmNoLCBpbmRleClcbiAgICAgIDogdGhhdC5zbGljZShpbmRleCwgaW5kZXggKyBzZWFyY2gubGVuZ3RoKSA9PT0gc2VhcmNoO1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIEIuMi4zLjEyIFN0cmluZy5wcm90b3R5cGUuc3RyaWtlKClcbnJlcXVpcmUoJy4vX3N0cmluZy1odG1sJykoJ3N0cmlrZScsIGZ1bmN0aW9uIChjcmVhdGVIVE1MKSB7XG4gIHJldHVybiBmdW5jdGlvbiBzdHJpa2UoKSB7XG4gICAgcmV0dXJuIGNyZWF0ZUhUTUwodGhpcywgJ3N0cmlrZScsICcnLCAnJyk7XG4gIH07XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIEIuMi4zLjEzIFN0cmluZy5wcm90b3R5cGUuc3ViKClcbnJlcXVpcmUoJy4vX3N0cmluZy1odG1sJykoJ3N1YicsIGZ1bmN0aW9uIChjcmVhdGVIVE1MKSB7XG4gIHJldHVybiBmdW5jdGlvbiBzdWIoKSB7XG4gICAgcmV0dXJuIGNyZWF0ZUhUTUwodGhpcywgJ3N1YicsICcnLCAnJyk7XG4gIH07XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIEIuMi4zLjE0IFN0cmluZy5wcm90b3R5cGUuc3VwKClcbnJlcXVpcmUoJy4vX3N0cmluZy1odG1sJykoJ3N1cCcsIGZ1bmN0aW9uIChjcmVhdGVIVE1MKSB7XG4gIHJldHVybiBmdW5jdGlvbiBzdXAoKSB7XG4gICAgcmV0dXJuIGNyZWF0ZUhUTUwodGhpcywgJ3N1cCcsICcnLCAnJyk7XG4gIH07XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIDIxLjEuMy4yNSBTdHJpbmcucHJvdG90eXBlLnRyaW0oKVxucmVxdWlyZSgnLi9fc3RyaW5nLXRyaW0nKSgndHJpbScsIGZ1bmN0aW9uICgkdHJpbSkge1xuICByZXR1cm4gZnVuY3Rpb24gdHJpbSgpIHtcbiAgICByZXR1cm4gJHRyaW0odGhpcywgMyk7XG4gIH07XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIEVDTUFTY3JpcHQgNiBzeW1ib2xzIHNoaW1cbnZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciBoYXMgPSByZXF1aXJlKCcuL19oYXMnKTtcbnZhciBERVNDUklQVE9SUyA9IHJlcXVpcmUoJy4vX2Rlc2NyaXB0b3JzJyk7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIHJlZGVmaW5lID0gcmVxdWlyZSgnLi9fcmVkZWZpbmUnKTtcbnZhciBNRVRBID0gcmVxdWlyZSgnLi9fbWV0YScpLktFWTtcbnZhciAkZmFpbHMgPSByZXF1aXJlKCcuL19mYWlscycpO1xudmFyIHNoYXJlZCA9IHJlcXVpcmUoJy4vX3NoYXJlZCcpO1xudmFyIHNldFRvU3RyaW5nVGFnID0gcmVxdWlyZSgnLi9fc2V0LXRvLXN0cmluZy10YWcnKTtcbnZhciB1aWQgPSByZXF1aXJlKCcuL191aWQnKTtcbnZhciB3a3MgPSByZXF1aXJlKCcuL193a3MnKTtcbnZhciB3a3NFeHQgPSByZXF1aXJlKCcuL193a3MtZXh0Jyk7XG52YXIgd2tzRGVmaW5lID0gcmVxdWlyZSgnLi9fd2tzLWRlZmluZScpO1xudmFyIGVudW1LZXlzID0gcmVxdWlyZSgnLi9fZW51bS1rZXlzJyk7XG52YXIgaXNBcnJheSA9IHJlcXVpcmUoJy4vX2lzLWFycmF5Jyk7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciBpc09iamVjdCA9IHJlcXVpcmUoJy4vX2lzLW9iamVjdCcpO1xudmFyIHRvSU9iamVjdCA9IHJlcXVpcmUoJy4vX3RvLWlvYmplY3QnKTtcbnZhciB0b1ByaW1pdGl2ZSA9IHJlcXVpcmUoJy4vX3RvLXByaW1pdGl2ZScpO1xudmFyIGNyZWF0ZURlc2MgPSByZXF1aXJlKCcuL19wcm9wZXJ0eS1kZXNjJyk7XG52YXIgX2NyZWF0ZSA9IHJlcXVpcmUoJy4vX29iamVjdC1jcmVhdGUnKTtcbnZhciBnT1BORXh0ID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcG4tZXh0Jyk7XG52YXIgJEdPUEQgPSByZXF1aXJlKCcuL19vYmplY3QtZ29wZCcpO1xudmFyICREUCA9IHJlcXVpcmUoJy4vX29iamVjdC1kcCcpO1xudmFyICRrZXlzID0gcmVxdWlyZSgnLi9fb2JqZWN0LWtleXMnKTtcbnZhciBnT1BEID0gJEdPUEQuZjtcbnZhciBkUCA9ICREUC5mO1xudmFyIGdPUE4gPSBnT1BORXh0LmY7XG52YXIgJFN5bWJvbCA9IGdsb2JhbC5TeW1ib2w7XG52YXIgJEpTT04gPSBnbG9iYWwuSlNPTjtcbnZhciBfc3RyaW5naWZ5ID0gJEpTT04gJiYgJEpTT04uc3RyaW5naWZ5O1xudmFyIFBST1RPVFlQRSA9ICdwcm90b3R5cGUnO1xudmFyIEhJRERFTiA9IHdrcygnX2hpZGRlbicpO1xudmFyIFRPX1BSSU1JVElWRSA9IHdrcygndG9QcmltaXRpdmUnKTtcbnZhciBpc0VudW0gPSB7fS5wcm9wZXJ0eUlzRW51bWVyYWJsZTtcbnZhciBTeW1ib2xSZWdpc3RyeSA9IHNoYXJlZCgnc3ltYm9sLXJlZ2lzdHJ5Jyk7XG52YXIgQWxsU3ltYm9scyA9IHNoYXJlZCgnc3ltYm9scycpO1xudmFyIE9QU3ltYm9scyA9IHNoYXJlZCgnb3Atc3ltYm9scycpO1xudmFyIE9iamVjdFByb3RvID0gT2JqZWN0W1BST1RPVFlQRV07XG52YXIgVVNFX05BVElWRSA9IHR5cGVvZiAkU3ltYm9sID09ICdmdW5jdGlvbic7XG52YXIgUU9iamVjdCA9IGdsb2JhbC5RT2JqZWN0O1xuLy8gRG9uJ3QgdXNlIHNldHRlcnMgaW4gUXQgU2NyaXB0LCBodHRwczovL2dpdGh1Yi5jb20vemxvaXJvY2svY29yZS1qcy9pc3N1ZXMvMTczXG52YXIgc2V0dGVyID0gIVFPYmplY3QgfHwgIVFPYmplY3RbUFJPVE9UWVBFXSB8fCAhUU9iamVjdFtQUk9UT1RZUEVdLmZpbmRDaGlsZDtcblxuLy8gZmFsbGJhY2sgZm9yIG9sZCBBbmRyb2lkLCBodHRwczovL2NvZGUuZ29vZ2xlLmNvbS9wL3Y4L2lzc3Vlcy9kZXRhaWw/aWQ9Njg3XG52YXIgc2V0U3ltYm9sRGVzYyA9IERFU0NSSVBUT1JTICYmICRmYWlscyhmdW5jdGlvbiAoKSB7XG4gIHJldHVybiBfY3JlYXRlKGRQKHt9LCAnYScsIHtcbiAgICBnZXQ6IGZ1bmN0aW9uICgpIHsgcmV0dXJuIGRQKHRoaXMsICdhJywgeyB2YWx1ZTogNyB9KS5hOyB9XG4gIH0pKS5hICE9IDc7XG59KSA/IGZ1bmN0aW9uIChpdCwga2V5LCBEKSB7XG4gIHZhciBwcm90b0Rlc2MgPSBnT1BEKE9iamVjdFByb3RvLCBrZXkpO1xuICBpZiAocHJvdG9EZXNjKSBkZWxldGUgT2JqZWN0UHJvdG9ba2V5XTtcbiAgZFAoaXQsIGtleSwgRCk7XG4gIGlmIChwcm90b0Rlc2MgJiYgaXQgIT09IE9iamVjdFByb3RvKSBkUChPYmplY3RQcm90bywga2V5LCBwcm90b0Rlc2MpO1xufSA6IGRQO1xuXG52YXIgd3JhcCA9IGZ1bmN0aW9uICh0YWcpIHtcbiAgdmFyIHN5bSA9IEFsbFN5bWJvbHNbdGFnXSA9IF9jcmVhdGUoJFN5bWJvbFtQUk9UT1RZUEVdKTtcbiAgc3ltLl9rID0gdGFnO1xuICByZXR1cm4gc3ltO1xufTtcblxudmFyIGlzU3ltYm9sID0gVVNFX05BVElWRSAmJiB0eXBlb2YgJFN5bWJvbC5pdGVyYXRvciA9PSAnc3ltYm9sJyA/IGZ1bmN0aW9uIChpdCkge1xuICByZXR1cm4gdHlwZW9mIGl0ID09ICdzeW1ib2wnO1xufSA6IGZ1bmN0aW9uIChpdCkge1xuICByZXR1cm4gaXQgaW5zdGFuY2VvZiAkU3ltYm9sO1xufTtcblxudmFyICRkZWZpbmVQcm9wZXJ0eSA9IGZ1bmN0aW9uIGRlZmluZVByb3BlcnR5KGl0LCBrZXksIEQpIHtcbiAgaWYgKGl0ID09PSBPYmplY3RQcm90bykgJGRlZmluZVByb3BlcnR5KE9QU3ltYm9scywga2V5LCBEKTtcbiAgYW5PYmplY3QoaXQpO1xuICBrZXkgPSB0b1ByaW1pdGl2ZShrZXksIHRydWUpO1xuICBhbk9iamVjdChEKTtcbiAgaWYgKGhhcyhBbGxTeW1ib2xzLCBrZXkpKSB7XG4gICAgaWYgKCFELmVudW1lcmFibGUpIHtcbiAgICAgIGlmICghaGFzKGl0LCBISURERU4pKSBkUChpdCwgSElEREVOLCBjcmVhdGVEZXNjKDEsIHt9KSk7XG4gICAgICBpdFtISURERU5dW2tleV0gPSB0cnVlO1xuICAgIH0gZWxzZSB7XG4gICAgICBpZiAoaGFzKGl0LCBISURERU4pICYmIGl0W0hJRERFTl1ba2V5XSkgaXRbSElEREVOXVtrZXldID0gZmFsc2U7XG4gICAgICBEID0gX2NyZWF0ZShELCB7IGVudW1lcmFibGU6IGNyZWF0ZURlc2MoMCwgZmFsc2UpIH0pO1xuICAgIH0gcmV0dXJuIHNldFN5bWJvbERlc2MoaXQsIGtleSwgRCk7XG4gIH0gcmV0dXJuIGRQKGl0LCBrZXksIEQpO1xufTtcbnZhciAkZGVmaW5lUHJvcGVydGllcyA9IGZ1bmN0aW9uIGRlZmluZVByb3BlcnRpZXMoaXQsIFApIHtcbiAgYW5PYmplY3QoaXQpO1xuICB2YXIga2V5cyA9IGVudW1LZXlzKFAgPSB0b0lPYmplY3QoUCkpO1xuICB2YXIgaSA9IDA7XG4gIHZhciBsID0ga2V5cy5sZW5ndGg7XG4gIHZhciBrZXk7XG4gIHdoaWxlIChsID4gaSkgJGRlZmluZVByb3BlcnR5KGl0LCBrZXkgPSBrZXlzW2krK10sIFBba2V5XSk7XG4gIHJldHVybiBpdDtcbn07XG52YXIgJGNyZWF0ZSA9IGZ1bmN0aW9uIGNyZWF0ZShpdCwgUCkge1xuICByZXR1cm4gUCA9PT0gdW5kZWZpbmVkID8gX2NyZWF0ZShpdCkgOiAkZGVmaW5lUHJvcGVydGllcyhfY3JlYXRlKGl0KSwgUCk7XG59O1xudmFyICRwcm9wZXJ0eUlzRW51bWVyYWJsZSA9IGZ1bmN0aW9uIHByb3BlcnR5SXNFbnVtZXJhYmxlKGtleSkge1xuICB2YXIgRSA9IGlzRW51bS5jYWxsKHRoaXMsIGtleSA9IHRvUHJpbWl0aXZlKGtleSwgdHJ1ZSkpO1xuICBpZiAodGhpcyA9PT0gT2JqZWN0UHJvdG8gJiYgaGFzKEFsbFN5bWJvbHMsIGtleSkgJiYgIWhhcyhPUFN5bWJvbHMsIGtleSkpIHJldHVybiBmYWxzZTtcbiAgcmV0dXJuIEUgfHwgIWhhcyh0aGlzLCBrZXkpIHx8ICFoYXMoQWxsU3ltYm9scywga2V5KSB8fCBoYXModGhpcywgSElEREVOKSAmJiB0aGlzW0hJRERFTl1ba2V5XSA/IEUgOiB0cnVlO1xufTtcbnZhciAkZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yID0gZnVuY3Rpb24gZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKGl0LCBrZXkpIHtcbiAgaXQgPSB0b0lPYmplY3QoaXQpO1xuICBrZXkgPSB0b1ByaW1pdGl2ZShrZXksIHRydWUpO1xuICBpZiAoaXQgPT09IE9iamVjdFByb3RvICYmIGhhcyhBbGxTeW1ib2xzLCBrZXkpICYmICFoYXMoT1BTeW1ib2xzLCBrZXkpKSByZXR1cm47XG4gIHZhciBEID0gZ09QRChpdCwga2V5KTtcbiAgaWYgKEQgJiYgaGFzKEFsbFN5bWJvbHMsIGtleSkgJiYgIShoYXMoaXQsIEhJRERFTikgJiYgaXRbSElEREVOXVtrZXldKSkgRC5lbnVtZXJhYmxlID0gdHJ1ZTtcbiAgcmV0dXJuIEQ7XG59O1xudmFyICRnZXRPd25Qcm9wZXJ0eU5hbWVzID0gZnVuY3Rpb24gZ2V0T3duUHJvcGVydHlOYW1lcyhpdCkge1xuICB2YXIgbmFtZXMgPSBnT1BOKHRvSU9iamVjdChpdCkpO1xuICB2YXIgcmVzdWx0ID0gW107XG4gIHZhciBpID0gMDtcbiAgdmFyIGtleTtcbiAgd2hpbGUgKG5hbWVzLmxlbmd0aCA+IGkpIHtcbiAgICBpZiAoIWhhcyhBbGxTeW1ib2xzLCBrZXkgPSBuYW1lc1tpKytdKSAmJiBrZXkgIT0gSElEREVOICYmIGtleSAhPSBNRVRBKSByZXN1bHQucHVzaChrZXkpO1xuICB9IHJldHVybiByZXN1bHQ7XG59O1xudmFyICRnZXRPd25Qcm9wZXJ0eVN5bWJvbHMgPSBmdW5jdGlvbiBnZXRPd25Qcm9wZXJ0eVN5bWJvbHMoaXQpIHtcbiAgdmFyIElTX09QID0gaXQgPT09IE9iamVjdFByb3RvO1xuICB2YXIgbmFtZXMgPSBnT1BOKElTX09QID8gT1BTeW1ib2xzIDogdG9JT2JqZWN0KGl0KSk7XG4gIHZhciByZXN1bHQgPSBbXTtcbiAgdmFyIGkgPSAwO1xuICB2YXIga2V5O1xuICB3aGlsZSAobmFtZXMubGVuZ3RoID4gaSkge1xuICAgIGlmIChoYXMoQWxsU3ltYm9scywga2V5ID0gbmFtZXNbaSsrXSkgJiYgKElTX09QID8gaGFzKE9iamVjdFByb3RvLCBrZXkpIDogdHJ1ZSkpIHJlc3VsdC5wdXNoKEFsbFN5bWJvbHNba2V5XSk7XG4gIH0gcmV0dXJuIHJlc3VsdDtcbn07XG5cbi8vIDE5LjQuMS4xIFN5bWJvbChbZGVzY3JpcHRpb25dKVxuaWYgKCFVU0VfTkFUSVZFKSB7XG4gICRTeW1ib2wgPSBmdW5jdGlvbiBTeW1ib2woKSB7XG4gICAgaWYgKHRoaXMgaW5zdGFuY2VvZiAkU3ltYm9sKSB0aHJvdyBUeXBlRXJyb3IoJ1N5bWJvbCBpcyBub3QgYSBjb25zdHJ1Y3RvciEnKTtcbiAgICB2YXIgdGFnID0gdWlkKGFyZ3VtZW50cy5sZW5ndGggPiAwID8gYXJndW1lbnRzWzBdIDogdW5kZWZpbmVkKTtcbiAgICB2YXIgJHNldCA9IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgaWYgKHRoaXMgPT09IE9iamVjdFByb3RvKSAkc2V0LmNhbGwoT1BTeW1ib2xzLCB2YWx1ZSk7XG4gICAgICBpZiAoaGFzKHRoaXMsIEhJRERFTikgJiYgaGFzKHRoaXNbSElEREVOXSwgdGFnKSkgdGhpc1tISURERU5dW3RhZ10gPSBmYWxzZTtcbiAgICAgIHNldFN5bWJvbERlc2ModGhpcywgdGFnLCBjcmVhdGVEZXNjKDEsIHZhbHVlKSk7XG4gICAgfTtcbiAgICBpZiAoREVTQ1JJUFRPUlMgJiYgc2V0dGVyKSBzZXRTeW1ib2xEZXNjKE9iamVjdFByb3RvLCB0YWcsIHsgY29uZmlndXJhYmxlOiB0cnVlLCBzZXQ6ICRzZXQgfSk7XG4gICAgcmV0dXJuIHdyYXAodGFnKTtcbiAgfTtcbiAgcmVkZWZpbmUoJFN5bWJvbFtQUk9UT1RZUEVdLCAndG9TdHJpbmcnLCBmdW5jdGlvbiB0b1N0cmluZygpIHtcbiAgICByZXR1cm4gdGhpcy5faztcbiAgfSk7XG5cbiAgJEdPUEQuZiA9ICRnZXRPd25Qcm9wZXJ0eURlc2NyaXB0b3I7XG4gICREUC5mID0gJGRlZmluZVByb3BlcnR5O1xuICByZXF1aXJlKCcuL19vYmplY3QtZ29wbicpLmYgPSBnT1BORXh0LmYgPSAkZ2V0T3duUHJvcGVydHlOYW1lcztcbiAgcmVxdWlyZSgnLi9fb2JqZWN0LXBpZScpLmYgPSAkcHJvcGVydHlJc0VudW1lcmFibGU7XG4gIHJlcXVpcmUoJy4vX29iamVjdC1nb3BzJykuZiA9ICRnZXRPd25Qcm9wZXJ0eVN5bWJvbHM7XG5cbiAgaWYgKERFU0NSSVBUT1JTICYmICFyZXF1aXJlKCcuL19saWJyYXJ5JykpIHtcbiAgICByZWRlZmluZShPYmplY3RQcm90bywgJ3Byb3BlcnR5SXNFbnVtZXJhYmxlJywgJHByb3BlcnR5SXNFbnVtZXJhYmxlLCB0cnVlKTtcbiAgfVxuXG4gIHdrc0V4dC5mID0gZnVuY3Rpb24gKG5hbWUpIHtcbiAgICByZXR1cm4gd3JhcCh3a3MobmFtZSkpO1xuICB9O1xufVxuXG4kZXhwb3J0KCRleHBvcnQuRyArICRleHBvcnQuVyArICRleHBvcnQuRiAqICFVU0VfTkFUSVZFLCB7IFN5bWJvbDogJFN5bWJvbCB9KTtcblxuZm9yICh2YXIgZXM2U3ltYm9scyA9IChcbiAgLy8gMTkuNC4yLjIsIDE5LjQuMi4zLCAxOS40LjIuNCwgMTkuNC4yLjYsIDE5LjQuMi44LCAxOS40LjIuOSwgMTkuNC4yLjEwLCAxOS40LjIuMTEsIDE5LjQuMi4xMiwgMTkuNC4yLjEzLCAxOS40LjIuMTRcbiAgJ2hhc0luc3RhbmNlLGlzQ29uY2F0U3ByZWFkYWJsZSxpdGVyYXRvcixtYXRjaCxyZXBsYWNlLHNlYXJjaCxzcGVjaWVzLHNwbGl0LHRvUHJpbWl0aXZlLHRvU3RyaW5nVGFnLHVuc2NvcGFibGVzJ1xuKS5zcGxpdCgnLCcpLCBqID0gMDsgZXM2U3ltYm9scy5sZW5ndGggPiBqOyl3a3MoZXM2U3ltYm9sc1tqKytdKTtcblxuZm9yICh2YXIgd2VsbEtub3duU3ltYm9scyA9ICRrZXlzKHdrcy5zdG9yZSksIGsgPSAwOyB3ZWxsS25vd25TeW1ib2xzLmxlbmd0aCA+IGs7KSB3a3NEZWZpbmUod2VsbEtub3duU3ltYm9sc1trKytdKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMgKyAkZXhwb3J0LkYgKiAhVVNFX05BVElWRSwgJ1N5bWJvbCcsIHtcbiAgLy8gMTkuNC4yLjEgU3ltYm9sLmZvcihrZXkpXG4gICdmb3InOiBmdW5jdGlvbiAoa2V5KSB7XG4gICAgcmV0dXJuIGhhcyhTeW1ib2xSZWdpc3RyeSwga2V5ICs9ICcnKVxuICAgICAgPyBTeW1ib2xSZWdpc3RyeVtrZXldXG4gICAgICA6IFN5bWJvbFJlZ2lzdHJ5W2tleV0gPSAkU3ltYm9sKGtleSk7XG4gIH0sXG4gIC8vIDE5LjQuMi41IFN5bWJvbC5rZXlGb3Ioc3ltKVxuICBrZXlGb3I6IGZ1bmN0aW9uIGtleUZvcihzeW0pIHtcbiAgICBpZiAoIWlzU3ltYm9sKHN5bSkpIHRocm93IFR5cGVFcnJvcihzeW0gKyAnIGlzIG5vdCBhIHN5bWJvbCEnKTtcbiAgICBmb3IgKHZhciBrZXkgaW4gU3ltYm9sUmVnaXN0cnkpIGlmIChTeW1ib2xSZWdpc3RyeVtrZXldID09PSBzeW0pIHJldHVybiBrZXk7XG4gIH0sXG4gIHVzZVNldHRlcjogZnVuY3Rpb24gKCkgeyBzZXR0ZXIgPSB0cnVlOyB9LFxuICB1c2VTaW1wbGU6IGZ1bmN0aW9uICgpIHsgc2V0dGVyID0gZmFsc2U7IH1cbn0pO1xuXG4kZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqICFVU0VfTkFUSVZFLCAnT2JqZWN0Jywge1xuICAvLyAxOS4xLjIuMiBPYmplY3QuY3JlYXRlKE8gWywgUHJvcGVydGllc10pXG4gIGNyZWF0ZTogJGNyZWF0ZSxcbiAgLy8gMTkuMS4yLjQgT2JqZWN0LmRlZmluZVByb3BlcnR5KE8sIFAsIEF0dHJpYnV0ZXMpXG4gIGRlZmluZVByb3BlcnR5OiAkZGVmaW5lUHJvcGVydHksXG4gIC8vIDE5LjEuMi4zIE9iamVjdC5kZWZpbmVQcm9wZXJ0aWVzKE8sIFByb3BlcnRpZXMpXG4gIGRlZmluZVByb3BlcnRpZXM6ICRkZWZpbmVQcm9wZXJ0aWVzLFxuICAvLyAxOS4xLjIuNiBPYmplY3QuZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yKE8sIFApXG4gIGdldE93blByb3BlcnR5RGVzY3JpcHRvcjogJGdldE93blByb3BlcnR5RGVzY3JpcHRvcixcbiAgLy8gMTkuMS4yLjcgT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXMoTylcbiAgZ2V0T3duUHJvcGVydHlOYW1lczogJGdldE93blByb3BlcnR5TmFtZXMsXG4gIC8vIDE5LjEuMi44IE9iamVjdC5nZXRPd25Qcm9wZXJ0eVN5bWJvbHMoTylcbiAgZ2V0T3duUHJvcGVydHlTeW1ib2xzOiAkZ2V0T3duUHJvcGVydHlTeW1ib2xzXG59KTtcblxuLy8gMjQuMy4yIEpTT04uc3RyaW5naWZ5KHZhbHVlIFssIHJlcGxhY2VyIFssIHNwYWNlXV0pXG4kSlNPTiAmJiAkZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqICghVVNFX05BVElWRSB8fCAkZmFpbHMoZnVuY3Rpb24gKCkge1xuICB2YXIgUyA9ICRTeW1ib2woKTtcbiAgLy8gTVMgRWRnZSBjb252ZXJ0cyBzeW1ib2wgdmFsdWVzIHRvIEpTT04gYXMge31cbiAgLy8gV2ViS2l0IGNvbnZlcnRzIHN5bWJvbCB2YWx1ZXMgdG8gSlNPTiBhcyBudWxsXG4gIC8vIFY4IHRocm93cyBvbiBib3hlZCBzeW1ib2xzXG4gIHJldHVybiBfc3RyaW5naWZ5KFtTXSkgIT0gJ1tudWxsXScgfHwgX3N0cmluZ2lmeSh7IGE6IFMgfSkgIT0gJ3t9JyB8fCBfc3RyaW5naWZ5KE9iamVjdChTKSkgIT0gJ3t9Jztcbn0pKSwgJ0pTT04nLCB7XG4gIHN0cmluZ2lmeTogZnVuY3Rpb24gc3RyaW5naWZ5KGl0KSB7XG4gICAgdmFyIGFyZ3MgPSBbaXRdO1xuICAgIHZhciBpID0gMTtcbiAgICB2YXIgcmVwbGFjZXIsICRyZXBsYWNlcjtcbiAgICB3aGlsZSAoYXJndW1lbnRzLmxlbmd0aCA+IGkpIGFyZ3MucHVzaChhcmd1bWVudHNbaSsrXSk7XG4gICAgJHJlcGxhY2VyID0gcmVwbGFjZXIgPSBhcmdzWzFdO1xuICAgIGlmICghaXNPYmplY3QocmVwbGFjZXIpICYmIGl0ID09PSB1bmRlZmluZWQgfHwgaXNTeW1ib2woaXQpKSByZXR1cm47IC8vIElFOCByZXR1cm5zIHN0cmluZyBvbiB1bmRlZmluZWRcbiAgICBpZiAoIWlzQXJyYXkocmVwbGFjZXIpKSByZXBsYWNlciA9IGZ1bmN0aW9uIChrZXksIHZhbHVlKSB7XG4gICAgICBpZiAodHlwZW9mICRyZXBsYWNlciA9PSAnZnVuY3Rpb24nKSB2YWx1ZSA9ICRyZXBsYWNlci5jYWxsKHRoaXMsIGtleSwgdmFsdWUpO1xuICAgICAgaWYgKCFpc1N5bWJvbCh2YWx1ZSkpIHJldHVybiB2YWx1ZTtcbiAgICB9O1xuICAgIGFyZ3NbMV0gPSByZXBsYWNlcjtcbiAgICByZXR1cm4gX3N0cmluZ2lmeS5hcHBseSgkSlNPTiwgYXJncyk7XG4gIH1cbn0pO1xuXG4vLyAxOS40LjMuNCBTeW1ib2wucHJvdG90eXBlW0BAdG9QcmltaXRpdmVdKGhpbnQpXG4kU3ltYm9sW1BST1RPVFlQRV1bVE9fUFJJTUlUSVZFXSB8fCByZXF1aXJlKCcuL19oaWRlJykoJFN5bWJvbFtQUk9UT1RZUEVdLCBUT19QUklNSVRJVkUsICRTeW1ib2xbUFJPVE9UWVBFXS52YWx1ZU9mKTtcbi8vIDE5LjQuMy41IFN5bWJvbC5wcm90b3R5cGVbQEB0b1N0cmluZ1RhZ11cbnNldFRvU3RyaW5nVGFnKCRTeW1ib2wsICdTeW1ib2wnKTtcbi8vIDIwLjIuMS45IE1hdGhbQEB0b1N0cmluZ1RhZ11cbnNldFRvU3RyaW5nVGFnKE1hdGgsICdNYXRoJywgdHJ1ZSk7XG4vLyAyNC4zLjMgSlNPTltAQHRvU3RyaW5nVGFnXVxuc2V0VG9TdHJpbmdUYWcoZ2xvYmFsLkpTT04sICdKU09OJywgdHJ1ZSk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICR0eXBlZCA9IHJlcXVpcmUoJy4vX3R5cGVkJyk7XG52YXIgYnVmZmVyID0gcmVxdWlyZSgnLi9fdHlwZWQtYnVmZmVyJyk7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciB0b0Fic29sdXRlSW5kZXggPSByZXF1aXJlKCcuL190by1hYnNvbHV0ZS1pbmRleCcpO1xudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG52YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcbnZhciBBcnJheUJ1ZmZlciA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpLkFycmF5QnVmZmVyO1xudmFyIHNwZWNpZXNDb25zdHJ1Y3RvciA9IHJlcXVpcmUoJy4vX3NwZWNpZXMtY29uc3RydWN0b3InKTtcbnZhciAkQXJyYXlCdWZmZXIgPSBidWZmZXIuQXJyYXlCdWZmZXI7XG52YXIgJERhdGFWaWV3ID0gYnVmZmVyLkRhdGFWaWV3O1xudmFyICRpc1ZpZXcgPSAkdHlwZWQuQUJWICYmIEFycmF5QnVmZmVyLmlzVmlldztcbnZhciAkc2xpY2UgPSAkQXJyYXlCdWZmZXIucHJvdG90eXBlLnNsaWNlO1xudmFyIFZJRVcgPSAkdHlwZWQuVklFVztcbnZhciBBUlJBWV9CVUZGRVIgPSAnQXJyYXlCdWZmZXInO1xuXG4kZXhwb3J0KCRleHBvcnQuRyArICRleHBvcnQuVyArICRleHBvcnQuRiAqIChBcnJheUJ1ZmZlciAhPT0gJEFycmF5QnVmZmVyKSwgeyBBcnJheUJ1ZmZlcjogJEFycmF5QnVmZmVyIH0pO1xuXG4kZXhwb3J0KCRleHBvcnQuUyArICRleHBvcnQuRiAqICEkdHlwZWQuQ09OU1RSLCBBUlJBWV9CVUZGRVIsIHtcbiAgLy8gMjQuMS4zLjEgQXJyYXlCdWZmZXIuaXNWaWV3KGFyZylcbiAgaXNWaWV3OiBmdW5jdGlvbiBpc1ZpZXcoaXQpIHtcbiAgICByZXR1cm4gJGlzVmlldyAmJiAkaXNWaWV3KGl0KSB8fCBpc09iamVjdChpdCkgJiYgVklFVyBpbiBpdDtcbiAgfVxufSk7XG5cbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5VICsgJGV4cG9ydC5GICogcmVxdWlyZSgnLi9fZmFpbHMnKShmdW5jdGlvbiAoKSB7XG4gIHJldHVybiAhbmV3ICRBcnJheUJ1ZmZlcigyKS5zbGljZSgxLCB1bmRlZmluZWQpLmJ5dGVMZW5ndGg7XG59KSwgQVJSQVlfQlVGRkVSLCB7XG4gIC8vIDI0LjEuNC4zIEFycmF5QnVmZmVyLnByb3RvdHlwZS5zbGljZShzdGFydCwgZW5kKVxuICBzbGljZTogZnVuY3Rpb24gc2xpY2Uoc3RhcnQsIGVuZCkge1xuICAgIGlmICgkc2xpY2UgIT09IHVuZGVmaW5lZCAmJiBlbmQgPT09IHVuZGVmaW5lZCkgcmV0dXJuICRzbGljZS5jYWxsKGFuT2JqZWN0KHRoaXMpLCBzdGFydCk7IC8vIEZGIGZpeFxuICAgIHZhciBsZW4gPSBhbk9iamVjdCh0aGlzKS5ieXRlTGVuZ3RoO1xuICAgIHZhciBmaXJzdCA9IHRvQWJzb2x1dGVJbmRleChzdGFydCwgbGVuKTtcbiAgICB2YXIgZmluID0gdG9BYnNvbHV0ZUluZGV4KGVuZCA9PT0gdW5kZWZpbmVkID8gbGVuIDogZW5kLCBsZW4pO1xuICAgIHZhciByZXN1bHQgPSBuZXcgKHNwZWNpZXNDb25zdHJ1Y3Rvcih0aGlzLCAkQXJyYXlCdWZmZXIpKSh0b0xlbmd0aChmaW4gLSBmaXJzdCkpO1xuICAgIHZhciB2aWV3UyA9IG5ldyAkRGF0YVZpZXcodGhpcyk7XG4gICAgdmFyIHZpZXdUID0gbmV3ICREYXRhVmlldyhyZXN1bHQpO1xuICAgIHZhciBpbmRleCA9IDA7XG4gICAgd2hpbGUgKGZpcnN0IDwgZmluKSB7XG4gICAgICB2aWV3VC5zZXRVaW50OChpbmRleCsrLCB2aWV3Uy5nZXRVaW50OChmaXJzdCsrKSk7XG4gICAgfSByZXR1cm4gcmVzdWx0O1xuICB9XG59KTtcblxucmVxdWlyZSgnLi9fc2V0LXNwZWNpZXMnKShBUlJBWV9CVUZGRVIpO1xuIiwidmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbiRleHBvcnQoJGV4cG9ydC5HICsgJGV4cG9ydC5XICsgJGV4cG9ydC5GICogIXJlcXVpcmUoJy4vX3R5cGVkJykuQUJWLCB7XG4gIERhdGFWaWV3OiByZXF1aXJlKCcuL190eXBlZC1idWZmZXInKS5EYXRhVmlld1xufSk7XG4iLCJyZXF1aXJlKCcuL190eXBlZC1hcnJheScpKCdGbG9hdDMyJywgNCwgZnVuY3Rpb24gKGluaXQpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIEZsb2F0MzJBcnJheShkYXRhLCBieXRlT2Zmc2V0LCBsZW5ndGgpIHtcbiAgICByZXR1cm4gaW5pdCh0aGlzLCBkYXRhLCBieXRlT2Zmc2V0LCBsZW5ndGgpO1xuICB9O1xufSk7XG4iLCJyZXF1aXJlKCcuL190eXBlZC1hcnJheScpKCdGbG9hdDY0JywgOCwgZnVuY3Rpb24gKGluaXQpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIEZsb2F0NjRBcnJheShkYXRhLCBieXRlT2Zmc2V0LCBsZW5ndGgpIHtcbiAgICByZXR1cm4gaW5pdCh0aGlzLCBkYXRhLCBieXRlT2Zmc2V0LCBsZW5ndGgpO1xuICB9O1xufSk7XG4iLCJyZXF1aXJlKCcuL190eXBlZC1hcnJheScpKCdJbnQxNicsIDIsIGZ1bmN0aW9uIChpbml0KSB7XG4gIHJldHVybiBmdW5jdGlvbiBJbnQxNkFycmF5KGRhdGEsIGJ5dGVPZmZzZXQsIGxlbmd0aCkge1xuICAgIHJldHVybiBpbml0KHRoaXMsIGRhdGEsIGJ5dGVPZmZzZXQsIGxlbmd0aCk7XG4gIH07XG59KTtcbiIsInJlcXVpcmUoJy4vX3R5cGVkLWFycmF5JykoJ0ludDMyJywgNCwgZnVuY3Rpb24gKGluaXQpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIEludDMyQXJyYXkoZGF0YSwgYnl0ZU9mZnNldCwgbGVuZ3RoKSB7XG4gICAgcmV0dXJuIGluaXQodGhpcywgZGF0YSwgYnl0ZU9mZnNldCwgbGVuZ3RoKTtcbiAgfTtcbn0pO1xuIiwicmVxdWlyZSgnLi9fdHlwZWQtYXJyYXknKSgnSW50OCcsIDEsIGZ1bmN0aW9uIChpbml0KSB7XG4gIHJldHVybiBmdW5jdGlvbiBJbnQ4QXJyYXkoZGF0YSwgYnl0ZU9mZnNldCwgbGVuZ3RoKSB7XG4gICAgcmV0dXJuIGluaXQodGhpcywgZGF0YSwgYnl0ZU9mZnNldCwgbGVuZ3RoKTtcbiAgfTtcbn0pO1xuIiwicmVxdWlyZSgnLi9fdHlwZWQtYXJyYXknKSgnVWludDE2JywgMiwgZnVuY3Rpb24gKGluaXQpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIFVpbnQxNkFycmF5KGRhdGEsIGJ5dGVPZmZzZXQsIGxlbmd0aCkge1xuICAgIHJldHVybiBpbml0KHRoaXMsIGRhdGEsIGJ5dGVPZmZzZXQsIGxlbmd0aCk7XG4gIH07XG59KTtcbiIsInJlcXVpcmUoJy4vX3R5cGVkLWFycmF5JykoJ1VpbnQzMicsIDQsIGZ1bmN0aW9uIChpbml0KSB7XG4gIHJldHVybiBmdW5jdGlvbiBVaW50MzJBcnJheShkYXRhLCBieXRlT2Zmc2V0LCBsZW5ndGgpIHtcbiAgICByZXR1cm4gaW5pdCh0aGlzLCBkYXRhLCBieXRlT2Zmc2V0LCBsZW5ndGgpO1xuICB9O1xufSk7XG4iLCJyZXF1aXJlKCcuL190eXBlZC1hcnJheScpKCdVaW50OCcsIDEsIGZ1bmN0aW9uIChpbml0KSB7XG4gIHJldHVybiBmdW5jdGlvbiBVaW50OEFycmF5KGRhdGEsIGJ5dGVPZmZzZXQsIGxlbmd0aCkge1xuICAgIHJldHVybiBpbml0KHRoaXMsIGRhdGEsIGJ5dGVPZmZzZXQsIGxlbmd0aCk7XG4gIH07XG59KTtcbiIsInJlcXVpcmUoJy4vX3R5cGVkLWFycmF5JykoJ1VpbnQ4JywgMSwgZnVuY3Rpb24gKGluaXQpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIFVpbnQ4Q2xhbXBlZEFycmF5KGRhdGEsIGJ5dGVPZmZzZXQsIGxlbmd0aCkge1xuICAgIHJldHVybiBpbml0KHRoaXMsIGRhdGEsIGJ5dGVPZmZzZXQsIGxlbmd0aCk7XG4gIH07XG59LCB0cnVlKTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciBnbG9iYWwgPSByZXF1aXJlKCcuL19nbG9iYWwnKTtcbnZhciBlYWNoID0gcmVxdWlyZSgnLi9fYXJyYXktbWV0aG9kcycpKDApO1xudmFyIHJlZGVmaW5lID0gcmVxdWlyZSgnLi9fcmVkZWZpbmUnKTtcbnZhciBtZXRhID0gcmVxdWlyZSgnLi9fbWV0YScpO1xudmFyIGFzc2lnbiA9IHJlcXVpcmUoJy4vX29iamVjdC1hc3NpZ24nKTtcbnZhciB3ZWFrID0gcmVxdWlyZSgnLi9fY29sbGVjdGlvbi13ZWFrJyk7XG52YXIgaXNPYmplY3QgPSByZXF1aXJlKCcuL19pcy1vYmplY3QnKTtcbnZhciB2YWxpZGF0ZSA9IHJlcXVpcmUoJy4vX3ZhbGlkYXRlLWNvbGxlY3Rpb24nKTtcbnZhciBOQVRJVkVfV0VBS19NQVAgPSByZXF1aXJlKCcuL192YWxpZGF0ZS1jb2xsZWN0aW9uJyk7XG52YXIgSVNfSUUxMSA9ICFnbG9iYWwuQWN0aXZlWE9iamVjdCAmJiAnQWN0aXZlWE9iamVjdCcgaW4gZ2xvYmFsO1xudmFyIFdFQUtfTUFQID0gJ1dlYWtNYXAnO1xudmFyIGdldFdlYWsgPSBtZXRhLmdldFdlYWs7XG52YXIgaXNFeHRlbnNpYmxlID0gT2JqZWN0LmlzRXh0ZW5zaWJsZTtcbnZhciB1bmNhdWdodEZyb3plblN0b3JlID0gd2Vhay51ZnN0b3JlO1xudmFyIEludGVybmFsTWFwO1xuXG52YXIgd3JhcHBlciA9IGZ1bmN0aW9uIChnZXQpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIFdlYWtNYXAoKSB7XG4gICAgcmV0dXJuIGdldCh0aGlzLCBhcmd1bWVudHMubGVuZ3RoID4gMCA/IGFyZ3VtZW50c1swXSA6IHVuZGVmaW5lZCk7XG4gIH07XG59O1xuXG52YXIgbWV0aG9kcyA9IHtcbiAgLy8gMjMuMy4zLjMgV2Vha01hcC5wcm90b3R5cGUuZ2V0KGtleSlcbiAgZ2V0OiBmdW5jdGlvbiBnZXQoa2V5KSB7XG4gICAgaWYgKGlzT2JqZWN0KGtleSkpIHtcbiAgICAgIHZhciBkYXRhID0gZ2V0V2VhayhrZXkpO1xuICAgICAgaWYgKGRhdGEgPT09IHRydWUpIHJldHVybiB1bmNhdWdodEZyb3plblN0b3JlKHZhbGlkYXRlKHRoaXMsIFdFQUtfTUFQKSkuZ2V0KGtleSk7XG4gICAgICByZXR1cm4gZGF0YSA/IGRhdGFbdGhpcy5faV0gOiB1bmRlZmluZWQ7XG4gICAgfVxuICB9LFxuICAvLyAyMy4zLjMuNSBXZWFrTWFwLnByb3RvdHlwZS5zZXQoa2V5LCB2YWx1ZSlcbiAgc2V0OiBmdW5jdGlvbiBzZXQoa2V5LCB2YWx1ZSkge1xuICAgIHJldHVybiB3ZWFrLmRlZih2YWxpZGF0ZSh0aGlzLCBXRUFLX01BUCksIGtleSwgdmFsdWUpO1xuICB9XG59O1xuXG4vLyAyMy4zIFdlYWtNYXAgT2JqZWN0c1xudmFyICRXZWFrTWFwID0gbW9kdWxlLmV4cG9ydHMgPSByZXF1aXJlKCcuL19jb2xsZWN0aW9uJykoV0VBS19NQVAsIHdyYXBwZXIsIG1ldGhvZHMsIHdlYWssIHRydWUsIHRydWUpO1xuXG4vLyBJRTExIFdlYWtNYXAgZnJvemVuIGtleXMgZml4XG5pZiAoTkFUSVZFX1dFQUtfTUFQICYmIElTX0lFMTEpIHtcbiAgSW50ZXJuYWxNYXAgPSB3ZWFrLmdldENvbnN0cnVjdG9yKHdyYXBwZXIsIFdFQUtfTUFQKTtcbiAgYXNzaWduKEludGVybmFsTWFwLnByb3RvdHlwZSwgbWV0aG9kcyk7XG4gIG1ldGEuTkVFRCA9IHRydWU7XG4gIGVhY2goWydkZWxldGUnLCAnaGFzJywgJ2dldCcsICdzZXQnXSwgZnVuY3Rpb24gKGtleSkge1xuICAgIHZhciBwcm90byA9ICRXZWFrTWFwLnByb3RvdHlwZTtcbiAgICB2YXIgbWV0aG9kID0gcHJvdG9ba2V5XTtcbiAgICByZWRlZmluZShwcm90bywga2V5LCBmdW5jdGlvbiAoYSwgYikge1xuICAgICAgLy8gc3RvcmUgZnJvemVuIG9iamVjdHMgb24gaW50ZXJuYWwgd2Vha21hcCBzaGltXG4gICAgICBpZiAoaXNPYmplY3QoYSkgJiYgIWlzRXh0ZW5zaWJsZShhKSkge1xuICAgICAgICBpZiAoIXRoaXMuX2YpIHRoaXMuX2YgPSBuZXcgSW50ZXJuYWxNYXAoKTtcbiAgICAgICAgdmFyIHJlc3VsdCA9IHRoaXMuX2Zba2V5XShhLCBiKTtcbiAgICAgICAgcmV0dXJuIGtleSA9PSAnc2V0JyA/IHRoaXMgOiByZXN1bHQ7XG4gICAgICAvLyBzdG9yZSBhbGwgdGhlIHJlc3Qgb24gbmF0aXZlIHdlYWttYXBcbiAgICAgIH0gcmV0dXJuIG1ldGhvZC5jYWxsKHRoaXMsIGEsIGIpO1xuICAgIH0pO1xuICB9KTtcbn1cbiIsIid1c2Ugc3RyaWN0JztcbnZhciB3ZWFrID0gcmVxdWlyZSgnLi9fY29sbGVjdGlvbi13ZWFrJyk7XG52YXIgdmFsaWRhdGUgPSByZXF1aXJlKCcuL192YWxpZGF0ZS1jb2xsZWN0aW9uJyk7XG52YXIgV0VBS19TRVQgPSAnV2Vha1NldCc7XG5cbi8vIDIzLjQgV2Vha1NldCBPYmplY3RzXG5yZXF1aXJlKCcuL19jb2xsZWN0aW9uJykoV0VBS19TRVQsIGZ1bmN0aW9uIChnZXQpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIFdlYWtTZXQoKSB7IHJldHVybiBnZXQodGhpcywgYXJndW1lbnRzLmxlbmd0aCA+IDAgPyBhcmd1bWVudHNbMF0gOiB1bmRlZmluZWQpOyB9O1xufSwge1xuICAvLyAyMy40LjMuMSBXZWFrU2V0LnByb3RvdHlwZS5hZGQodmFsdWUpXG4gIGFkZDogZnVuY3Rpb24gYWRkKHZhbHVlKSB7XG4gICAgcmV0dXJuIHdlYWsuZGVmKHZhbGlkYXRlKHRoaXMsIFdFQUtfU0VUKSwgdmFsdWUsIHRydWUpO1xuICB9XG59LCB3ZWFrLCBmYWxzZSwgdHJ1ZSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL3Byb3Bvc2FsLWZsYXRNYXAvI3NlYy1BcnJheS5wcm90b3R5cGUuZmxhdE1hcFxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBmbGF0dGVuSW50b0FycmF5ID0gcmVxdWlyZSgnLi9fZmxhdHRlbi1pbnRvLWFycmF5Jyk7XG52YXIgdG9PYmplY3QgPSByZXF1aXJlKCcuL190by1vYmplY3QnKTtcbnZhciB0b0xlbmd0aCA9IHJlcXVpcmUoJy4vX3RvLWxlbmd0aCcpO1xudmFyIGFGdW5jdGlvbiA9IHJlcXVpcmUoJy4vX2EtZnVuY3Rpb24nKTtcbnZhciBhcnJheVNwZWNpZXNDcmVhdGUgPSByZXF1aXJlKCcuL19hcnJheS1zcGVjaWVzLWNyZWF0ZScpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCwgJ0FycmF5Jywge1xuICBmbGF0TWFwOiBmdW5jdGlvbiBmbGF0TWFwKGNhbGxiYWNrZm4gLyogLCB0aGlzQXJnICovKSB7XG4gICAgdmFyIE8gPSB0b09iamVjdCh0aGlzKTtcbiAgICB2YXIgc291cmNlTGVuLCBBO1xuICAgIGFGdW5jdGlvbihjYWxsYmFja2ZuKTtcbiAgICBzb3VyY2VMZW4gPSB0b0xlbmd0aChPLmxlbmd0aCk7XG4gICAgQSA9IGFycmF5U3BlY2llc0NyZWF0ZShPLCAwKTtcbiAgICBmbGF0dGVuSW50b0FycmF5KEEsIE8sIE8sIHNvdXJjZUxlbiwgMCwgMSwgY2FsbGJhY2tmbiwgYXJndW1lbnRzWzFdKTtcbiAgICByZXR1cm4gQTtcbiAgfVxufSk7XG5cbnJlcXVpcmUoJy4vX2FkZC10by11bnNjb3BhYmxlcycpKCdmbGF0TWFwJyk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL3Byb3Bvc2FsLWZsYXRNYXAvI3NlYy1BcnJheS5wcm90b3R5cGUuZmxhdHRlblxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBmbGF0dGVuSW50b0FycmF5ID0gcmVxdWlyZSgnLi9fZmxhdHRlbi1pbnRvLWFycmF5Jyk7XG52YXIgdG9PYmplY3QgPSByZXF1aXJlKCcuL190by1vYmplY3QnKTtcbnZhciB0b0xlbmd0aCA9IHJlcXVpcmUoJy4vX3RvLWxlbmd0aCcpO1xudmFyIHRvSW50ZWdlciA9IHJlcXVpcmUoJy4vX3RvLWludGVnZXInKTtcbnZhciBhcnJheVNwZWNpZXNDcmVhdGUgPSByZXF1aXJlKCcuL19hcnJheS1zcGVjaWVzLWNyZWF0ZScpO1xuXG4kZXhwb3J0KCRleHBvcnQuUCwgJ0FycmF5Jywge1xuICBmbGF0dGVuOiBmdW5jdGlvbiBmbGF0dGVuKC8qIGRlcHRoQXJnID0gMSAqLykge1xuICAgIHZhciBkZXB0aEFyZyA9IGFyZ3VtZW50c1swXTtcbiAgICB2YXIgTyA9IHRvT2JqZWN0KHRoaXMpO1xuICAgIHZhciBzb3VyY2VMZW4gPSB0b0xlbmd0aChPLmxlbmd0aCk7XG4gICAgdmFyIEEgPSBhcnJheVNwZWNpZXNDcmVhdGUoTywgMCk7XG4gICAgZmxhdHRlbkludG9BcnJheShBLCBPLCBPLCBzb3VyY2VMZW4sIDAsIGRlcHRoQXJnID09PSB1bmRlZmluZWQgPyAxIDogdG9JbnRlZ2VyKGRlcHRoQXJnKSk7XG4gICAgcmV0dXJuIEE7XG4gIH1cbn0pO1xuXG5yZXF1aXJlKCcuL19hZGQtdG8tdW5zY29wYWJsZXMnKSgnZmxhdHRlbicpO1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gaHR0cHM6Ly9naXRodWIuY29tL3RjMzkvQXJyYXkucHJvdG90eXBlLmluY2x1ZGVzXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRpbmNsdWRlcyA9IHJlcXVpcmUoJy4vX2FycmF5LWluY2x1ZGVzJykodHJ1ZSk7XG5cbiRleHBvcnQoJGV4cG9ydC5QLCAnQXJyYXknLCB7XG4gIGluY2x1ZGVzOiBmdW5jdGlvbiBpbmNsdWRlcyhlbCAvKiAsIGZyb21JbmRleCA9IDAgKi8pIHtcbiAgICByZXR1cm4gJGluY2x1ZGVzKHRoaXMsIGVsLCBhcmd1bWVudHMubGVuZ3RoID4gMSA/IGFyZ3VtZW50c1sxXSA6IHVuZGVmaW5lZCk7XG4gIH1cbn0pO1xuXG5yZXF1aXJlKCcuL19hZGQtdG8tdW5zY29wYWJsZXMnKSgnaW5jbHVkZXMnKTtcbiIsIi8vIGh0dHBzOi8vZ2l0aHViLmNvbS9yd2FsZHJvbi90YzM5LW5vdGVzL2Jsb2IvbWFzdGVyL2VzNi8yMDE0LTA5L3NlcHQtMjUubWQjNTEwLWdsb2JhbGFzYXAtZm9yLWVucXVldWluZy1hLW1pY3JvdGFza1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBtaWNyb3Rhc2sgPSByZXF1aXJlKCcuL19taWNyb3Rhc2snKSgpO1xudmFyIHByb2Nlc3MgPSByZXF1aXJlKCcuL19nbG9iYWwnKS5wcm9jZXNzO1xudmFyIGlzTm9kZSA9IHJlcXVpcmUoJy4vX2NvZicpKHByb2Nlc3MpID09ICdwcm9jZXNzJztcblxuJGV4cG9ydCgkZXhwb3J0LkcsIHtcbiAgYXNhcDogZnVuY3Rpb24gYXNhcChmbikge1xuICAgIHZhciBkb21haW4gPSBpc05vZGUgJiYgcHJvY2Vzcy5kb21haW47XG4gICAgbWljcm90YXNrKGRvbWFpbiA/IGRvbWFpbi5iaW5kKGZuKSA6IGZuKTtcbiAgfVxufSk7XG4iLCIvLyBodHRwczovL2dpdGh1Yi5jb20vbGpoYXJiL3Byb3Bvc2FsLWlzLWVycm9yXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGNvZiA9IHJlcXVpcmUoJy4vX2NvZicpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ0Vycm9yJywge1xuICBpc0Vycm9yOiBmdW5jdGlvbiBpc0Vycm9yKGl0KSB7XG4gICAgcmV0dXJuIGNvZihpdCkgPT09ICdFcnJvcic7XG4gIH1cbn0pO1xuIiwiLy8gaHR0cHM6Ly9naXRodWIuY29tL3RjMzkvcHJvcG9zYWwtZ2xvYmFsXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuRywgeyBnbG9iYWw6IHJlcXVpcmUoJy4vX2dsb2JhbCcpIH0pO1xuIiwiLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9wcm9wb3NhbC1zZXRtYXAtb2Zmcm9tLyNzZWMtbWFwLmZyb21cbnJlcXVpcmUoJy4vX3NldC1jb2xsZWN0aW9uLWZyb20nKSgnTWFwJyk7XG4iLCIvLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL3Byb3Bvc2FsLXNldG1hcC1vZmZyb20vI3NlYy1tYXAub2ZcbnJlcXVpcmUoJy4vX3NldC1jb2xsZWN0aW9uLW9mJykoJ01hcCcpO1xuIiwiLy8gaHR0cHM6Ly9naXRodWIuY29tL0RhdmlkQnJ1YW50L01hcC1TZXQucHJvdG90eXBlLnRvSlNPTlxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LlIsICdNYXAnLCB7IHRvSlNPTjogcmVxdWlyZSgnLi9fY29sbGVjdGlvbi10by1qc29uJykoJ01hcCcpIH0pO1xuIiwiLy8gaHR0cHM6Ly9yd2FsZHJvbi5naXRodWIuaW8vcHJvcG9zYWwtbWF0aC1leHRlbnNpb25zL1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywge1xuICBjbGFtcDogZnVuY3Rpb24gY2xhbXAoeCwgbG93ZXIsIHVwcGVyKSB7XG4gICAgcmV0dXJuIE1hdGgubWluKHVwcGVyLCBNYXRoLm1heChsb3dlciwgeCkpO1xuICB9XG59KTtcbiIsIi8vIGh0dHBzOi8vcndhbGRyb24uZ2l0aHViLmlvL3Byb3Bvc2FsLW1hdGgtZXh0ZW5zaW9ucy9cbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTWF0aCcsIHsgREVHX1BFUl9SQUQ6IE1hdGguUEkgLyAxODAgfSk7XG4iLCIvLyBodHRwczovL3J3YWxkcm9uLmdpdGh1Yi5pby9wcm9wb3NhbC1tYXRoLWV4dGVuc2lvbnMvXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIFJBRF9QRVJfREVHID0gMTgwIC8gTWF0aC5QSTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywge1xuICBkZWdyZWVzOiBmdW5jdGlvbiBkZWdyZWVzKHJhZGlhbnMpIHtcbiAgICByZXR1cm4gcmFkaWFucyAqIFJBRF9QRVJfREVHO1xuICB9XG59KTtcbiIsIi8vIGh0dHBzOi8vcndhbGRyb24uZ2l0aHViLmlvL3Byb3Bvc2FsLW1hdGgtZXh0ZW5zaW9ucy9cbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgc2NhbGUgPSByZXF1aXJlKCcuL19tYXRoLXNjYWxlJyk7XG52YXIgZnJvdW5kID0gcmVxdWlyZSgnLi9fbWF0aC1mcm91bmQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywge1xuICBmc2NhbGU6IGZ1bmN0aW9uIGZzY2FsZSh4LCBpbkxvdywgaW5IaWdoLCBvdXRMb3csIG91dEhpZ2gpIHtcbiAgICByZXR1cm4gZnJvdW5kKHNjYWxlKHgsIGluTG93LCBpbkhpZ2gsIG91dExvdywgb3V0SGlnaCkpO1xuICB9XG59KTtcbiIsIi8vIGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL0JyZW5kYW5FaWNoLzQyOTRkNWMyMTJhNmQyMjU0NzAzXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ01hdGgnLCB7XG4gIGlhZGRoOiBmdW5jdGlvbiBpYWRkaCh4MCwgeDEsIHkwLCB5MSkge1xuICAgIHZhciAkeDAgPSB4MCA+Pj4gMDtcbiAgICB2YXIgJHgxID0geDEgPj4+IDA7XG4gICAgdmFyICR5MCA9IHkwID4+PiAwO1xuICAgIHJldHVybiAkeDEgKyAoeTEgPj4+IDApICsgKCgkeDAgJiAkeTAgfCAoJHgwIHwgJHkwKSAmIH4oJHgwICsgJHkwID4+PiAwKSkgPj4+IDMxKSB8IDA7XG4gIH1cbn0pO1xuIiwiLy8gaHR0cHM6Ly9naXN0LmdpdGh1Yi5jb20vQnJlbmRhbkVpY2gvNDI5NGQ1YzIxMmE2ZDIyNTQ3MDNcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTWF0aCcsIHtcbiAgaW11bGg6IGZ1bmN0aW9uIGltdWxoKHUsIHYpIHtcbiAgICB2YXIgVUlOVDE2ID0gMHhmZmZmO1xuICAgIHZhciAkdSA9ICt1O1xuICAgIHZhciAkdiA9ICt2O1xuICAgIHZhciB1MCA9ICR1ICYgVUlOVDE2O1xuICAgIHZhciB2MCA9ICR2ICYgVUlOVDE2O1xuICAgIHZhciB1MSA9ICR1ID4+IDE2O1xuICAgIHZhciB2MSA9ICR2ID4+IDE2O1xuICAgIHZhciB0ID0gKHUxICogdjAgPj4+IDApICsgKHUwICogdjAgPj4+IDE2KTtcbiAgICByZXR1cm4gdTEgKiB2MSArICh0ID4+IDE2KSArICgodTAgKiB2MSA+Pj4gMCkgKyAodCAmIFVJTlQxNikgPj4gMTYpO1xuICB9XG59KTtcbiIsIi8vIGh0dHBzOi8vZ2lzdC5naXRodWIuY29tL0JyZW5kYW5FaWNoLzQyOTRkNWMyMTJhNmQyMjU0NzAzXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ01hdGgnLCB7XG4gIGlzdWJoOiBmdW5jdGlvbiBpc3ViaCh4MCwgeDEsIHkwLCB5MSkge1xuICAgIHZhciAkeDAgPSB4MCA+Pj4gMDtcbiAgICB2YXIgJHgxID0geDEgPj4+IDA7XG4gICAgdmFyICR5MCA9IHkwID4+PiAwO1xuICAgIHJldHVybiAkeDEgLSAoeTEgPj4+IDApIC0gKCh+JHgwICYgJHkwIHwgfigkeDAgXiAkeTApICYgJHgwIC0gJHkwID4+PiAwKSA+Pj4gMzEpIHwgMDtcbiAgfVxufSk7XG4iLCIvLyBodHRwczovL3J3YWxkcm9uLmdpdGh1Yi5pby9wcm9wb3NhbC1tYXRoLWV4dGVuc2lvbnMvXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ01hdGgnLCB7IFJBRF9QRVJfREVHOiAxODAgLyBNYXRoLlBJIH0pO1xuIiwiLy8gaHR0cHM6Ly9yd2FsZHJvbi5naXRodWIuaW8vcHJvcG9zYWwtbWF0aC1leHRlbnNpb25zL1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBERUdfUEVSX1JBRCA9IE1hdGguUEkgLyAxODA7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTWF0aCcsIHtcbiAgcmFkaWFuczogZnVuY3Rpb24gcmFkaWFucyhkZWdyZWVzKSB7XG4gICAgcmV0dXJuIGRlZ3JlZXMgKiBERUdfUEVSX1JBRDtcbiAgfVxufSk7XG4iLCIvLyBodHRwczovL3J3YWxkcm9uLmdpdGh1Yi5pby9wcm9wb3NhbC1tYXRoLWV4dGVuc2lvbnMvXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xuXG4kZXhwb3J0KCRleHBvcnQuUywgJ01hdGgnLCB7IHNjYWxlOiByZXF1aXJlKCcuL19tYXRoLXNjYWxlJykgfSk7XG4iLCIvLyBodHRwOi8vamZiYXN0aWVuLmdpdGh1Yi5pby9wYXBlcnMvTWF0aC5zaWduYml0Lmh0bWxcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnTWF0aCcsIHsgc2lnbmJpdDogZnVuY3Rpb24gc2lnbmJpdCh4KSB7XG4gIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1zZWxmLWNvbXBhcmVcbiAgcmV0dXJuICh4ID0gK3gpICE9IHggPyB4IDogeCA9PSAwID8gMSAvIHggPT0gSW5maW5pdHkgOiB4ID4gMDtcbn0gfSk7XG4iLCIvLyBodHRwczovL2dpc3QuZ2l0aHViLmNvbS9CcmVuZGFuRWljaC80Mjk0ZDVjMjEyYTZkMjI1NDcwM1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdNYXRoJywge1xuICB1bXVsaDogZnVuY3Rpb24gdW11bGgodSwgdikge1xuICAgIHZhciBVSU5UMTYgPSAweGZmZmY7XG4gICAgdmFyICR1ID0gK3U7XG4gICAgdmFyICR2ID0gK3Y7XG4gICAgdmFyIHUwID0gJHUgJiBVSU5UMTY7XG4gICAgdmFyIHYwID0gJHYgJiBVSU5UMTY7XG4gICAgdmFyIHUxID0gJHUgPj4+IDE2O1xuICAgIHZhciB2MSA9ICR2ID4+PiAxNjtcbiAgICB2YXIgdCA9ICh1MSAqIHYwID4+PiAwKSArICh1MCAqIHYwID4+PiAxNik7XG4gICAgcmV0dXJuIHUxICogdjEgKyAodCA+Pj4gMTYpICsgKCh1MCAqIHYxID4+PiAwKSArICh0ICYgVUlOVDE2KSA+Pj4gMTYpO1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0JztcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgdG9PYmplY3QgPSByZXF1aXJlKCcuL190by1vYmplY3QnKTtcbnZhciBhRnVuY3Rpb24gPSByZXF1aXJlKCcuL19hLWZ1bmN0aW9uJyk7XG52YXIgJGRlZmluZVByb3BlcnR5ID0gcmVxdWlyZSgnLi9fb2JqZWN0LWRwJyk7XG5cbi8vIEIuMi4yLjIgT2JqZWN0LnByb3RvdHlwZS5fX2RlZmluZUdldHRlcl9fKFAsIGdldHRlcilcbnJlcXVpcmUoJy4vX2Rlc2NyaXB0b3JzJykgJiYgJGV4cG9ydCgkZXhwb3J0LlAgKyByZXF1aXJlKCcuL19vYmplY3QtZm9yY2VkLXBhbScpLCAnT2JqZWN0Jywge1xuICBfX2RlZmluZUdldHRlcl9fOiBmdW5jdGlvbiBfX2RlZmluZUdldHRlcl9fKFAsIGdldHRlcikge1xuICAgICRkZWZpbmVQcm9wZXJ0eS5mKHRvT2JqZWN0KHRoaXMpLCBQLCB7IGdldDogYUZ1bmN0aW9uKGdldHRlciksIGVudW1lcmFibGU6IHRydWUsIGNvbmZpZ3VyYWJsZTogdHJ1ZSB9KTtcbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIHRvT2JqZWN0ID0gcmVxdWlyZSgnLi9fdG8tb2JqZWN0Jyk7XG52YXIgYUZ1bmN0aW9uID0gcmVxdWlyZSgnLi9fYS1mdW5jdGlvbicpO1xudmFyICRkZWZpbmVQcm9wZXJ0eSA9IHJlcXVpcmUoJy4vX29iamVjdC1kcCcpO1xuXG4vLyBCLjIuMi4zIE9iamVjdC5wcm90b3R5cGUuX19kZWZpbmVTZXR0ZXJfXyhQLCBzZXR0ZXIpXG5yZXF1aXJlKCcuL19kZXNjcmlwdG9ycycpICYmICRleHBvcnQoJGV4cG9ydC5QICsgcmVxdWlyZSgnLi9fb2JqZWN0LWZvcmNlZC1wYW0nKSwgJ09iamVjdCcsIHtcbiAgX19kZWZpbmVTZXR0ZXJfXzogZnVuY3Rpb24gX19kZWZpbmVTZXR0ZXJfXyhQLCBzZXR0ZXIpIHtcbiAgICAkZGVmaW5lUHJvcGVydHkuZih0b09iamVjdCh0aGlzKSwgUCwgeyBzZXQ6IGFGdW5jdGlvbihzZXR0ZXIpLCBlbnVtZXJhYmxlOiB0cnVlLCBjb25maWd1cmFibGU6IHRydWUgfSk7XG4gIH1cbn0pO1xuIiwiLy8gaHR0cHM6Ly9naXRodWIuY29tL3RjMzkvcHJvcG9zYWwtb2JqZWN0LXZhbHVlcy1lbnRyaWVzXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICRlbnRyaWVzID0gcmVxdWlyZSgnLi9fb2JqZWN0LXRvLWFycmF5JykodHJ1ZSk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnT2JqZWN0Jywge1xuICBlbnRyaWVzOiBmdW5jdGlvbiBlbnRyaWVzKGl0KSB7XG4gICAgcmV0dXJuICRlbnRyaWVzKGl0KTtcbiAgfVxufSk7XG4iLCIvLyBodHRwczovL2dpdGh1Yi5jb20vdGMzOS9wcm9wb3NhbC1vYmplY3QtZ2V0b3ducHJvcGVydHlkZXNjcmlwdG9yc1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBvd25LZXlzID0gcmVxdWlyZSgnLi9fb3duLWtleXMnKTtcbnZhciB0b0lPYmplY3QgPSByZXF1aXJlKCcuL190by1pb2JqZWN0Jyk7XG52YXIgZ09QRCA9IHJlcXVpcmUoJy4vX29iamVjdC1nb3BkJyk7XG52YXIgY3JlYXRlUHJvcGVydHkgPSByZXF1aXJlKCcuL19jcmVhdGUtcHJvcGVydHknKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdPYmplY3QnLCB7XG4gIGdldE93blByb3BlcnR5RGVzY3JpcHRvcnM6IGZ1bmN0aW9uIGdldE93blByb3BlcnR5RGVzY3JpcHRvcnMob2JqZWN0KSB7XG4gICAgdmFyIE8gPSB0b0lPYmplY3Qob2JqZWN0KTtcbiAgICB2YXIgZ2V0RGVzYyA9IGdPUEQuZjtcbiAgICB2YXIga2V5cyA9IG93bktleXMoTyk7XG4gICAgdmFyIHJlc3VsdCA9IHt9O1xuICAgIHZhciBpID0gMDtcbiAgICB2YXIga2V5LCBkZXNjO1xuICAgIHdoaWxlIChrZXlzLmxlbmd0aCA+IGkpIHtcbiAgICAgIGRlc2MgPSBnZXREZXNjKE8sIGtleSA9IGtleXNbaSsrXSk7XG4gICAgICBpZiAoZGVzYyAhPT0gdW5kZWZpbmVkKSBjcmVhdGVQcm9wZXJ0eShyZXN1bHQsIGtleSwgZGVzYyk7XG4gICAgfVxuICAgIHJldHVybiByZXN1bHQ7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIHRvUHJpbWl0aXZlID0gcmVxdWlyZSgnLi9fdG8tcHJpbWl0aXZlJyk7XG52YXIgZ2V0UHJvdG90eXBlT2YgPSByZXF1aXJlKCcuL19vYmplY3QtZ3BvJyk7XG52YXIgZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcGQnKS5mO1xuXG4vLyBCLjIuMi40IE9iamVjdC5wcm90b3R5cGUuX19sb29rdXBHZXR0ZXJfXyhQKVxucmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKSAmJiAkZXhwb3J0KCRleHBvcnQuUCArIHJlcXVpcmUoJy4vX29iamVjdC1mb3JjZWQtcGFtJyksICdPYmplY3QnLCB7XG4gIF9fbG9va3VwR2V0dGVyX186IGZ1bmN0aW9uIF9fbG9va3VwR2V0dGVyX18oUCkge1xuICAgIHZhciBPID0gdG9PYmplY3QodGhpcyk7XG4gICAgdmFyIEsgPSB0b1ByaW1pdGl2ZShQLCB0cnVlKTtcbiAgICB2YXIgRDtcbiAgICBkbyB7XG4gICAgICBpZiAoRCA9IGdldE93blByb3BlcnR5RGVzY3JpcHRvcihPLCBLKSkgcmV0dXJuIEQuZ2V0O1xuICAgIH0gd2hpbGUgKE8gPSBnZXRQcm90b3R5cGVPZihPKSk7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciB0b09iamVjdCA9IHJlcXVpcmUoJy4vX3RvLW9iamVjdCcpO1xudmFyIHRvUHJpbWl0aXZlID0gcmVxdWlyZSgnLi9fdG8tcHJpbWl0aXZlJyk7XG52YXIgZ2V0UHJvdG90eXBlT2YgPSByZXF1aXJlKCcuL19vYmplY3QtZ3BvJyk7XG52YXIgZ2V0T3duUHJvcGVydHlEZXNjcmlwdG9yID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdvcGQnKS5mO1xuXG4vLyBCLjIuMi41IE9iamVjdC5wcm90b3R5cGUuX19sb29rdXBTZXR0ZXJfXyhQKVxucmVxdWlyZSgnLi9fZGVzY3JpcHRvcnMnKSAmJiAkZXhwb3J0KCRleHBvcnQuUCArIHJlcXVpcmUoJy4vX29iamVjdC1mb3JjZWQtcGFtJyksICdPYmplY3QnLCB7XG4gIF9fbG9va3VwU2V0dGVyX186IGZ1bmN0aW9uIF9fbG9va3VwU2V0dGVyX18oUCkge1xuICAgIHZhciBPID0gdG9PYmplY3QodGhpcyk7XG4gICAgdmFyIEsgPSB0b1ByaW1pdGl2ZShQLCB0cnVlKTtcbiAgICB2YXIgRDtcbiAgICBkbyB7XG4gICAgICBpZiAoRCA9IGdldE93blByb3BlcnR5RGVzY3JpcHRvcihPLCBLKSkgcmV0dXJuIEQuc2V0O1xuICAgIH0gd2hpbGUgKE8gPSBnZXRQcm90b3R5cGVPZihPKSk7XG4gIH1cbn0pO1xuIiwiLy8gaHR0cHM6Ly9naXRodWIuY29tL3RjMzkvcHJvcG9zYWwtb2JqZWN0LXZhbHVlcy1lbnRyaWVzXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyICR2YWx1ZXMgPSByZXF1aXJlKCcuL19vYmplY3QtdG8tYXJyYXknKShmYWxzZSk7XG5cbiRleHBvcnQoJGV4cG9ydC5TLCAnT2JqZWN0Jywge1xuICB2YWx1ZXM6IGZ1bmN0aW9uIHZhbHVlcyhpdCkge1xuICAgIHJldHVybiAkdmFsdWVzKGl0KTtcbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBodHRwczovL2dpdGh1Yi5jb20vemVucGFyc2luZy9lcy1vYnNlcnZhYmxlXG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGdsb2JhbCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpO1xudmFyIGNvcmUgPSByZXF1aXJlKCcuL19jb3JlJyk7XG52YXIgbWljcm90YXNrID0gcmVxdWlyZSgnLi9fbWljcm90YXNrJykoKTtcbnZhciBPQlNFUlZBQkxFID0gcmVxdWlyZSgnLi9fd2tzJykoJ29ic2VydmFibGUnKTtcbnZhciBhRnVuY3Rpb24gPSByZXF1aXJlKCcuL19hLWZ1bmN0aW9uJyk7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciBhbkluc3RhbmNlID0gcmVxdWlyZSgnLi9fYW4taW5zdGFuY2UnKTtcbnZhciByZWRlZmluZUFsbCA9IHJlcXVpcmUoJy4vX3JlZGVmaW5lLWFsbCcpO1xudmFyIGhpZGUgPSByZXF1aXJlKCcuL19oaWRlJyk7XG52YXIgZm9yT2YgPSByZXF1aXJlKCcuL19mb3Itb2YnKTtcbnZhciBSRVRVUk4gPSBmb3JPZi5SRVRVUk47XG5cbnZhciBnZXRNZXRob2QgPSBmdW5jdGlvbiAoZm4pIHtcbiAgcmV0dXJuIGZuID09IG51bGwgPyB1bmRlZmluZWQgOiBhRnVuY3Rpb24oZm4pO1xufTtcblxudmFyIGNsZWFudXBTdWJzY3JpcHRpb24gPSBmdW5jdGlvbiAoc3Vic2NyaXB0aW9uKSB7XG4gIHZhciBjbGVhbnVwID0gc3Vic2NyaXB0aW9uLl9jO1xuICBpZiAoY2xlYW51cCkge1xuICAgIHN1YnNjcmlwdGlvbi5fYyA9IHVuZGVmaW5lZDtcbiAgICBjbGVhbnVwKCk7XG4gIH1cbn07XG5cbnZhciBzdWJzY3JpcHRpb25DbG9zZWQgPSBmdW5jdGlvbiAoc3Vic2NyaXB0aW9uKSB7XG4gIHJldHVybiBzdWJzY3JpcHRpb24uX28gPT09IHVuZGVmaW5lZDtcbn07XG5cbnZhciBjbG9zZVN1YnNjcmlwdGlvbiA9IGZ1bmN0aW9uIChzdWJzY3JpcHRpb24pIHtcbiAgaWYgKCFzdWJzY3JpcHRpb25DbG9zZWQoc3Vic2NyaXB0aW9uKSkge1xuICAgIHN1YnNjcmlwdGlvbi5fbyA9IHVuZGVmaW5lZDtcbiAgICBjbGVhbnVwU3Vic2NyaXB0aW9uKHN1YnNjcmlwdGlvbik7XG4gIH1cbn07XG5cbnZhciBTdWJzY3JpcHRpb24gPSBmdW5jdGlvbiAob2JzZXJ2ZXIsIHN1YnNjcmliZXIpIHtcbiAgYW5PYmplY3Qob2JzZXJ2ZXIpO1xuICB0aGlzLl9jID0gdW5kZWZpbmVkO1xuICB0aGlzLl9vID0gb2JzZXJ2ZXI7XG4gIG9ic2VydmVyID0gbmV3IFN1YnNjcmlwdGlvbk9ic2VydmVyKHRoaXMpO1xuICB0cnkge1xuICAgIHZhciBjbGVhbnVwID0gc3Vic2NyaWJlcihvYnNlcnZlcik7XG4gICAgdmFyIHN1YnNjcmlwdGlvbiA9IGNsZWFudXA7XG4gICAgaWYgKGNsZWFudXAgIT0gbnVsbCkge1xuICAgICAgaWYgKHR5cGVvZiBjbGVhbnVwLnVuc3Vic2NyaWJlID09PSAnZnVuY3Rpb24nKSBjbGVhbnVwID0gZnVuY3Rpb24gKCkgeyBzdWJzY3JpcHRpb24udW5zdWJzY3JpYmUoKTsgfTtcbiAgICAgIGVsc2UgYUZ1bmN0aW9uKGNsZWFudXApO1xuICAgICAgdGhpcy5fYyA9IGNsZWFudXA7XG4gICAgfVxuICB9IGNhdGNoIChlKSB7XG4gICAgb2JzZXJ2ZXIuZXJyb3IoZSk7XG4gICAgcmV0dXJuO1xuICB9IGlmIChzdWJzY3JpcHRpb25DbG9zZWQodGhpcykpIGNsZWFudXBTdWJzY3JpcHRpb24odGhpcyk7XG59O1xuXG5TdWJzY3JpcHRpb24ucHJvdG90eXBlID0gcmVkZWZpbmVBbGwoe30sIHtcbiAgdW5zdWJzY3JpYmU6IGZ1bmN0aW9uIHVuc3Vic2NyaWJlKCkgeyBjbG9zZVN1YnNjcmlwdGlvbih0aGlzKTsgfVxufSk7XG5cbnZhciBTdWJzY3JpcHRpb25PYnNlcnZlciA9IGZ1bmN0aW9uIChzdWJzY3JpcHRpb24pIHtcbiAgdGhpcy5fcyA9IHN1YnNjcmlwdGlvbjtcbn07XG5cblN1YnNjcmlwdGlvbk9ic2VydmVyLnByb3RvdHlwZSA9IHJlZGVmaW5lQWxsKHt9LCB7XG4gIG5leHQ6IGZ1bmN0aW9uIG5leHQodmFsdWUpIHtcbiAgICB2YXIgc3Vic2NyaXB0aW9uID0gdGhpcy5fcztcbiAgICBpZiAoIXN1YnNjcmlwdGlvbkNsb3NlZChzdWJzY3JpcHRpb24pKSB7XG4gICAgICB2YXIgb2JzZXJ2ZXIgPSBzdWJzY3JpcHRpb24uX287XG4gICAgICB0cnkge1xuICAgICAgICB2YXIgbSA9IGdldE1ldGhvZChvYnNlcnZlci5uZXh0KTtcbiAgICAgICAgaWYgKG0pIHJldHVybiBtLmNhbGwob2JzZXJ2ZXIsIHZhbHVlKTtcbiAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgdHJ5IHtcbiAgICAgICAgICBjbG9zZVN1YnNjcmlwdGlvbihzdWJzY3JpcHRpb24pO1xuICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgIHRocm93IGU7XG4gICAgICAgIH1cbiAgICAgIH1cbiAgICB9XG4gIH0sXG4gIGVycm9yOiBmdW5jdGlvbiBlcnJvcih2YWx1ZSkge1xuICAgIHZhciBzdWJzY3JpcHRpb24gPSB0aGlzLl9zO1xuICAgIGlmIChzdWJzY3JpcHRpb25DbG9zZWQoc3Vic2NyaXB0aW9uKSkgdGhyb3cgdmFsdWU7XG4gICAgdmFyIG9ic2VydmVyID0gc3Vic2NyaXB0aW9uLl9vO1xuICAgIHN1YnNjcmlwdGlvbi5fbyA9IHVuZGVmaW5lZDtcbiAgICB0cnkge1xuICAgICAgdmFyIG0gPSBnZXRNZXRob2Qob2JzZXJ2ZXIuZXJyb3IpO1xuICAgICAgaWYgKCFtKSB0aHJvdyB2YWx1ZTtcbiAgICAgIHZhbHVlID0gbS5jYWxsKG9ic2VydmVyLCB2YWx1ZSk7XG4gICAgfSBjYXRjaCAoZSkge1xuICAgICAgdHJ5IHtcbiAgICAgICAgY2xlYW51cFN1YnNjcmlwdGlvbihzdWJzY3JpcHRpb24pO1xuICAgICAgfSBmaW5hbGx5IHtcbiAgICAgICAgdGhyb3cgZTtcbiAgICAgIH1cbiAgICB9IGNsZWFudXBTdWJzY3JpcHRpb24oc3Vic2NyaXB0aW9uKTtcbiAgICByZXR1cm4gdmFsdWU7XG4gIH0sXG4gIGNvbXBsZXRlOiBmdW5jdGlvbiBjb21wbGV0ZSh2YWx1ZSkge1xuICAgIHZhciBzdWJzY3JpcHRpb24gPSB0aGlzLl9zO1xuICAgIGlmICghc3Vic2NyaXB0aW9uQ2xvc2VkKHN1YnNjcmlwdGlvbikpIHtcbiAgICAgIHZhciBvYnNlcnZlciA9IHN1YnNjcmlwdGlvbi5fbztcbiAgICAgIHN1YnNjcmlwdGlvbi5fbyA9IHVuZGVmaW5lZDtcbiAgICAgIHRyeSB7XG4gICAgICAgIHZhciBtID0gZ2V0TWV0aG9kKG9ic2VydmVyLmNvbXBsZXRlKTtcbiAgICAgICAgdmFsdWUgPSBtID8gbS5jYWxsKG9ic2VydmVyLCB2YWx1ZSkgOiB1bmRlZmluZWQ7XG4gICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY2xlYW51cFN1YnNjcmlwdGlvbihzdWJzY3JpcHRpb24pO1xuICAgICAgICB9IGZpbmFsbHkge1xuICAgICAgICAgIHRocm93IGU7XG4gICAgICAgIH1cbiAgICAgIH0gY2xlYW51cFN1YnNjcmlwdGlvbihzdWJzY3JpcHRpb24pO1xuICAgICAgcmV0dXJuIHZhbHVlO1xuICAgIH1cbiAgfVxufSk7XG5cbnZhciAkT2JzZXJ2YWJsZSA9IGZ1bmN0aW9uIE9ic2VydmFibGUoc3Vic2NyaWJlcikge1xuICBhbkluc3RhbmNlKHRoaXMsICRPYnNlcnZhYmxlLCAnT2JzZXJ2YWJsZScsICdfZicpLl9mID0gYUZ1bmN0aW9uKHN1YnNjcmliZXIpO1xufTtcblxucmVkZWZpbmVBbGwoJE9ic2VydmFibGUucHJvdG90eXBlLCB7XG4gIHN1YnNjcmliZTogZnVuY3Rpb24gc3Vic2NyaWJlKG9ic2VydmVyKSB7XG4gICAgcmV0dXJuIG5ldyBTdWJzY3JpcHRpb24ob2JzZXJ2ZXIsIHRoaXMuX2YpO1xuICB9LFxuICBmb3JFYWNoOiBmdW5jdGlvbiBmb3JFYWNoKGZuKSB7XG4gICAgdmFyIHRoYXQgPSB0aGlzO1xuICAgIHJldHVybiBuZXcgKGNvcmUuUHJvbWlzZSB8fCBnbG9iYWwuUHJvbWlzZSkoZnVuY3Rpb24gKHJlc29sdmUsIHJlamVjdCkge1xuICAgICAgYUZ1bmN0aW9uKGZuKTtcbiAgICAgIHZhciBzdWJzY3JpcHRpb24gPSB0aGF0LnN1YnNjcmliZSh7XG4gICAgICAgIG5leHQ6IGZ1bmN0aW9uICh2YWx1ZSkge1xuICAgICAgICAgIHRyeSB7XG4gICAgICAgICAgICByZXR1cm4gZm4odmFsdWUpO1xuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIHJlamVjdChlKTtcbiAgICAgICAgICAgIHN1YnNjcmlwdGlvbi51bnN1YnNjcmliZSgpO1xuICAgICAgICAgIH1cbiAgICAgICAgfSxcbiAgICAgICAgZXJyb3I6IHJlamVjdCxcbiAgICAgICAgY29tcGxldGU6IHJlc29sdmVcbiAgICAgIH0pO1xuICAgIH0pO1xuICB9XG59KTtcblxucmVkZWZpbmVBbGwoJE9ic2VydmFibGUsIHtcbiAgZnJvbTogZnVuY3Rpb24gZnJvbSh4KSB7XG4gICAgdmFyIEMgPSB0eXBlb2YgdGhpcyA9PT0gJ2Z1bmN0aW9uJyA/IHRoaXMgOiAkT2JzZXJ2YWJsZTtcbiAgICB2YXIgbWV0aG9kID0gZ2V0TWV0aG9kKGFuT2JqZWN0KHgpW09CU0VSVkFCTEVdKTtcbiAgICBpZiAobWV0aG9kKSB7XG4gICAgICB2YXIgb2JzZXJ2YWJsZSA9IGFuT2JqZWN0KG1ldGhvZC5jYWxsKHgpKTtcbiAgICAgIHJldHVybiBvYnNlcnZhYmxlLmNvbnN0cnVjdG9yID09PSBDID8gb2JzZXJ2YWJsZSA6IG5ldyBDKGZ1bmN0aW9uIChvYnNlcnZlcikge1xuICAgICAgICByZXR1cm4gb2JzZXJ2YWJsZS5zdWJzY3JpYmUob2JzZXJ2ZXIpO1xuICAgICAgfSk7XG4gICAgfVxuICAgIHJldHVybiBuZXcgQyhmdW5jdGlvbiAob2JzZXJ2ZXIpIHtcbiAgICAgIHZhciBkb25lID0gZmFsc2U7XG4gICAgICBtaWNyb3Rhc2soZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoIWRvbmUpIHtcbiAgICAgICAgICB0cnkge1xuICAgICAgICAgICAgaWYgKGZvck9mKHgsIGZhbHNlLCBmdW5jdGlvbiAoaXQpIHtcbiAgICAgICAgICAgICAgb2JzZXJ2ZXIubmV4dChpdCk7XG4gICAgICAgICAgICAgIGlmIChkb25lKSByZXR1cm4gUkVUVVJOO1xuICAgICAgICAgICAgfSkgPT09IFJFVFVSTikgcmV0dXJuO1xuICAgICAgICAgIH0gY2F0Y2ggKGUpIHtcbiAgICAgICAgICAgIGlmIChkb25lKSB0aHJvdyBlO1xuICAgICAgICAgICAgb2JzZXJ2ZXIuZXJyb3IoZSk7XG4gICAgICAgICAgICByZXR1cm47XG4gICAgICAgICAgfSBvYnNlcnZlci5jb21wbGV0ZSgpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHJldHVybiBmdW5jdGlvbiAoKSB7IGRvbmUgPSB0cnVlOyB9O1xuICAgIH0pO1xuICB9LFxuICBvZjogZnVuY3Rpb24gb2YoKSB7XG4gICAgZm9yICh2YXIgaSA9IDAsIGwgPSBhcmd1bWVudHMubGVuZ3RoLCBpdGVtcyA9IG5ldyBBcnJheShsKTsgaSA8IGw7KSBpdGVtc1tpXSA9IGFyZ3VtZW50c1tpKytdO1xuICAgIHJldHVybiBuZXcgKHR5cGVvZiB0aGlzID09PSAnZnVuY3Rpb24nID8gdGhpcyA6ICRPYnNlcnZhYmxlKShmdW5jdGlvbiAob2JzZXJ2ZXIpIHtcbiAgICAgIHZhciBkb25lID0gZmFsc2U7XG4gICAgICBtaWNyb3Rhc2soZnVuY3Rpb24gKCkge1xuICAgICAgICBpZiAoIWRvbmUpIHtcbiAgICAgICAgICBmb3IgKHZhciBqID0gMDsgaiA8IGl0ZW1zLmxlbmd0aDsgKytqKSB7XG4gICAgICAgICAgICBvYnNlcnZlci5uZXh0KGl0ZW1zW2pdKTtcbiAgICAgICAgICAgIGlmIChkb25lKSByZXR1cm47XG4gICAgICAgICAgfSBvYnNlcnZlci5jb21wbGV0ZSgpO1xuICAgICAgICB9XG4gICAgICB9KTtcbiAgICAgIHJldHVybiBmdW5jdGlvbiAoKSB7IGRvbmUgPSB0cnVlOyB9O1xuICAgIH0pO1xuICB9XG59KTtcblxuaGlkZSgkT2JzZXJ2YWJsZS5wcm90b3R5cGUsIE9CU0VSVkFCTEUsIGZ1bmN0aW9uICgpIHsgcmV0dXJuIHRoaXM7IH0pO1xuXG4kZXhwb3J0KCRleHBvcnQuRywgeyBPYnNlcnZhYmxlOiAkT2JzZXJ2YWJsZSB9KTtcblxucmVxdWlyZSgnLi9fc2V0LXNwZWNpZXMnKSgnT2JzZXJ2YWJsZScpO1xuIiwiLy8gaHR0cHM6Ly9naXRodWIuY29tL3RjMzkvcHJvcG9zYWwtcHJvbWlzZS1maW5hbGx5XG4ndXNlIHN0cmljdCc7XG52YXIgJGV4cG9ydCA9IHJlcXVpcmUoJy4vX2V4cG9ydCcpO1xudmFyIGNvcmUgPSByZXF1aXJlKCcuL19jb3JlJyk7XG52YXIgZ2xvYmFsID0gcmVxdWlyZSgnLi9fZ2xvYmFsJyk7XG52YXIgc3BlY2llc0NvbnN0cnVjdG9yID0gcmVxdWlyZSgnLi9fc3BlY2llcy1jb25zdHJ1Y3RvcicpO1xudmFyIHByb21pc2VSZXNvbHZlID0gcmVxdWlyZSgnLi9fcHJvbWlzZS1yZXNvbHZlJyk7XG5cbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5SLCAnUHJvbWlzZScsIHsgJ2ZpbmFsbHknOiBmdW5jdGlvbiAob25GaW5hbGx5KSB7XG4gIHZhciBDID0gc3BlY2llc0NvbnN0cnVjdG9yKHRoaXMsIGNvcmUuUHJvbWlzZSB8fCBnbG9iYWwuUHJvbWlzZSk7XG4gIHZhciBpc0Z1bmN0aW9uID0gdHlwZW9mIG9uRmluYWxseSA9PSAnZnVuY3Rpb24nO1xuICByZXR1cm4gdGhpcy50aGVuKFxuICAgIGlzRnVuY3Rpb24gPyBmdW5jdGlvbiAoeCkge1xuICAgICAgcmV0dXJuIHByb21pc2VSZXNvbHZlKEMsIG9uRmluYWxseSgpKS50aGVuKGZ1bmN0aW9uICgpIHsgcmV0dXJuIHg7IH0pO1xuICAgIH0gOiBvbkZpbmFsbHksXG4gICAgaXNGdW5jdGlvbiA/IGZ1bmN0aW9uIChlKSB7XG4gICAgICByZXR1cm4gcHJvbWlzZVJlc29sdmUoQywgb25GaW5hbGx5KCkpLnRoZW4oZnVuY3Rpb24gKCkgeyB0aHJvdyBlOyB9KTtcbiAgICB9IDogb25GaW5hbGx5XG4gICk7XG59IH0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gaHR0cHM6Ly9naXRodWIuY29tL3RjMzkvcHJvcG9zYWwtcHJvbWlzZS10cnlcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgbmV3UHJvbWlzZUNhcGFiaWxpdHkgPSByZXF1aXJlKCcuL19uZXctcHJvbWlzZS1jYXBhYmlsaXR5Jyk7XG52YXIgcGVyZm9ybSA9IHJlcXVpcmUoJy4vX3BlcmZvcm0nKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdQcm9taXNlJywgeyAndHJ5JzogZnVuY3Rpb24gKGNhbGxiYWNrZm4pIHtcbiAgdmFyIHByb21pc2VDYXBhYmlsaXR5ID0gbmV3UHJvbWlzZUNhcGFiaWxpdHkuZih0aGlzKTtcbiAgdmFyIHJlc3VsdCA9IHBlcmZvcm0oY2FsbGJhY2tmbik7XG4gIChyZXN1bHQuZSA/IHByb21pc2VDYXBhYmlsaXR5LnJlamVjdCA6IHByb21pc2VDYXBhYmlsaXR5LnJlc29sdmUpKHJlc3VsdC52KTtcbiAgcmV0dXJuIHByb21pc2VDYXBhYmlsaXR5LnByb21pc2U7XG59IH0pO1xuIiwidmFyIG1ldGFkYXRhID0gcmVxdWlyZSgnLi9fbWV0YWRhdGEnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIHRvTWV0YUtleSA9IG1ldGFkYXRhLmtleTtcbnZhciBvcmRpbmFyeURlZmluZU93bk1ldGFkYXRhID0gbWV0YWRhdGEuc2V0O1xuXG5tZXRhZGF0YS5leHAoeyBkZWZpbmVNZXRhZGF0YTogZnVuY3Rpb24gZGVmaW5lTWV0YWRhdGEobWV0YWRhdGFLZXksIG1ldGFkYXRhVmFsdWUsIHRhcmdldCwgdGFyZ2V0S2V5KSB7XG4gIG9yZGluYXJ5RGVmaW5lT3duTWV0YWRhdGEobWV0YWRhdGFLZXksIG1ldGFkYXRhVmFsdWUsIGFuT2JqZWN0KHRhcmdldCksIHRvTWV0YUtleSh0YXJnZXRLZXkpKTtcbn0gfSk7XG4iLCJ2YXIgbWV0YWRhdGEgPSByZXF1aXJlKCcuL19tZXRhZGF0YScpO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgdG9NZXRhS2V5ID0gbWV0YWRhdGEua2V5O1xudmFyIGdldE9yQ3JlYXRlTWV0YWRhdGFNYXAgPSBtZXRhZGF0YS5tYXA7XG52YXIgc3RvcmUgPSBtZXRhZGF0YS5zdG9yZTtcblxubWV0YWRhdGEuZXhwKHsgZGVsZXRlTWV0YWRhdGE6IGZ1bmN0aW9uIGRlbGV0ZU1ldGFkYXRhKG1ldGFkYXRhS2V5LCB0YXJnZXQgLyogLCB0YXJnZXRLZXkgKi8pIHtcbiAgdmFyIHRhcmdldEtleSA9IGFyZ3VtZW50cy5sZW5ndGggPCAzID8gdW5kZWZpbmVkIDogdG9NZXRhS2V5KGFyZ3VtZW50c1syXSk7XG4gIHZhciBtZXRhZGF0YU1hcCA9IGdldE9yQ3JlYXRlTWV0YWRhdGFNYXAoYW5PYmplY3QodGFyZ2V0KSwgdGFyZ2V0S2V5LCBmYWxzZSk7XG4gIGlmIChtZXRhZGF0YU1hcCA9PT0gdW5kZWZpbmVkIHx8ICFtZXRhZGF0YU1hcFsnZGVsZXRlJ10obWV0YWRhdGFLZXkpKSByZXR1cm4gZmFsc2U7XG4gIGlmIChtZXRhZGF0YU1hcC5zaXplKSByZXR1cm4gdHJ1ZTtcbiAgdmFyIHRhcmdldE1ldGFkYXRhID0gc3RvcmUuZ2V0KHRhcmdldCk7XG4gIHRhcmdldE1ldGFkYXRhWydkZWxldGUnXSh0YXJnZXRLZXkpO1xuICByZXR1cm4gISF0YXJnZXRNZXRhZGF0YS5zaXplIHx8IHN0b3JlWydkZWxldGUnXSh0YXJnZXQpO1xufSB9KTtcbiIsInZhciBTZXQgPSByZXF1aXJlKCcuL2VzNi5zZXQnKTtcbnZhciBmcm9tID0gcmVxdWlyZSgnLi9fYXJyYXktZnJvbS1pdGVyYWJsZScpO1xudmFyIG1ldGFkYXRhID0gcmVxdWlyZSgnLi9fbWV0YWRhdGEnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIGdldFByb3RvdHlwZU9mID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdwbycpO1xudmFyIG9yZGluYXJ5T3duTWV0YWRhdGFLZXlzID0gbWV0YWRhdGEua2V5cztcbnZhciB0b01ldGFLZXkgPSBtZXRhZGF0YS5rZXk7XG5cbnZhciBvcmRpbmFyeU1ldGFkYXRhS2V5cyA9IGZ1bmN0aW9uIChPLCBQKSB7XG4gIHZhciBvS2V5cyA9IG9yZGluYXJ5T3duTWV0YWRhdGFLZXlzKE8sIFApO1xuICB2YXIgcGFyZW50ID0gZ2V0UHJvdG90eXBlT2YoTyk7XG4gIGlmIChwYXJlbnQgPT09IG51bGwpIHJldHVybiBvS2V5cztcbiAgdmFyIHBLZXlzID0gb3JkaW5hcnlNZXRhZGF0YUtleXMocGFyZW50LCBQKTtcbiAgcmV0dXJuIHBLZXlzLmxlbmd0aCA/IG9LZXlzLmxlbmd0aCA/IGZyb20obmV3IFNldChvS2V5cy5jb25jYXQocEtleXMpKSkgOiBwS2V5cyA6IG9LZXlzO1xufTtcblxubWV0YWRhdGEuZXhwKHsgZ2V0TWV0YWRhdGFLZXlzOiBmdW5jdGlvbiBnZXRNZXRhZGF0YUtleXModGFyZ2V0IC8qICwgdGFyZ2V0S2V5ICovKSB7XG4gIHJldHVybiBvcmRpbmFyeU1ldGFkYXRhS2V5cyhhbk9iamVjdCh0YXJnZXQpLCBhcmd1bWVudHMubGVuZ3RoIDwgMiA/IHVuZGVmaW5lZCA6IHRvTWV0YUtleShhcmd1bWVudHNbMV0pKTtcbn0gfSk7XG4iLCJ2YXIgbWV0YWRhdGEgPSByZXF1aXJlKCcuL19tZXRhZGF0YScpO1xudmFyIGFuT2JqZWN0ID0gcmVxdWlyZSgnLi9fYW4tb2JqZWN0Jyk7XG52YXIgZ2V0UHJvdG90eXBlT2YgPSByZXF1aXJlKCcuL19vYmplY3QtZ3BvJyk7XG52YXIgb3JkaW5hcnlIYXNPd25NZXRhZGF0YSA9IG1ldGFkYXRhLmhhcztcbnZhciBvcmRpbmFyeUdldE93bk1ldGFkYXRhID0gbWV0YWRhdGEuZ2V0O1xudmFyIHRvTWV0YUtleSA9IG1ldGFkYXRhLmtleTtcblxudmFyIG9yZGluYXJ5R2V0TWV0YWRhdGEgPSBmdW5jdGlvbiAoTWV0YWRhdGFLZXksIE8sIFApIHtcbiAgdmFyIGhhc093biA9IG9yZGluYXJ5SGFzT3duTWV0YWRhdGEoTWV0YWRhdGFLZXksIE8sIFApO1xuICBpZiAoaGFzT3duKSByZXR1cm4gb3JkaW5hcnlHZXRPd25NZXRhZGF0YShNZXRhZGF0YUtleSwgTywgUCk7XG4gIHZhciBwYXJlbnQgPSBnZXRQcm90b3R5cGVPZihPKTtcbiAgcmV0dXJuIHBhcmVudCAhPT0gbnVsbCA/IG9yZGluYXJ5R2V0TWV0YWRhdGEoTWV0YWRhdGFLZXksIHBhcmVudCwgUCkgOiB1bmRlZmluZWQ7XG59O1xuXG5tZXRhZGF0YS5leHAoeyBnZXRNZXRhZGF0YTogZnVuY3Rpb24gZ2V0TWV0YWRhdGEobWV0YWRhdGFLZXksIHRhcmdldCAvKiAsIHRhcmdldEtleSAqLykge1xuICByZXR1cm4gb3JkaW5hcnlHZXRNZXRhZGF0YShtZXRhZGF0YUtleSwgYW5PYmplY3QodGFyZ2V0KSwgYXJndW1lbnRzLmxlbmd0aCA8IDMgPyB1bmRlZmluZWQgOiB0b01ldGFLZXkoYXJndW1lbnRzWzJdKSk7XG59IH0pO1xuIiwidmFyIG1ldGFkYXRhID0gcmVxdWlyZSgnLi9fbWV0YWRhdGEnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIG9yZGluYXJ5T3duTWV0YWRhdGFLZXlzID0gbWV0YWRhdGEua2V5cztcbnZhciB0b01ldGFLZXkgPSBtZXRhZGF0YS5rZXk7XG5cbm1ldGFkYXRhLmV4cCh7IGdldE93bk1ldGFkYXRhS2V5czogZnVuY3Rpb24gZ2V0T3duTWV0YWRhdGFLZXlzKHRhcmdldCAvKiAsIHRhcmdldEtleSAqLykge1xuICByZXR1cm4gb3JkaW5hcnlPd25NZXRhZGF0YUtleXMoYW5PYmplY3QodGFyZ2V0KSwgYXJndW1lbnRzLmxlbmd0aCA8IDIgPyB1bmRlZmluZWQgOiB0b01ldGFLZXkoYXJndW1lbnRzWzFdKSk7XG59IH0pO1xuIiwidmFyIG1ldGFkYXRhID0gcmVxdWlyZSgnLi9fbWV0YWRhdGEnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIG9yZGluYXJ5R2V0T3duTWV0YWRhdGEgPSBtZXRhZGF0YS5nZXQ7XG52YXIgdG9NZXRhS2V5ID0gbWV0YWRhdGEua2V5O1xuXG5tZXRhZGF0YS5leHAoeyBnZXRPd25NZXRhZGF0YTogZnVuY3Rpb24gZ2V0T3duTWV0YWRhdGEobWV0YWRhdGFLZXksIHRhcmdldCAvKiAsIHRhcmdldEtleSAqLykge1xuICByZXR1cm4gb3JkaW5hcnlHZXRPd25NZXRhZGF0YShtZXRhZGF0YUtleSwgYW5PYmplY3QodGFyZ2V0KVxuICAgICwgYXJndW1lbnRzLmxlbmd0aCA8IDMgPyB1bmRlZmluZWQgOiB0b01ldGFLZXkoYXJndW1lbnRzWzJdKSk7XG59IH0pO1xuIiwidmFyIG1ldGFkYXRhID0gcmVxdWlyZSgnLi9fbWV0YWRhdGEnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIGdldFByb3RvdHlwZU9mID0gcmVxdWlyZSgnLi9fb2JqZWN0LWdwbycpO1xudmFyIG9yZGluYXJ5SGFzT3duTWV0YWRhdGEgPSBtZXRhZGF0YS5oYXM7XG52YXIgdG9NZXRhS2V5ID0gbWV0YWRhdGEua2V5O1xuXG52YXIgb3JkaW5hcnlIYXNNZXRhZGF0YSA9IGZ1bmN0aW9uIChNZXRhZGF0YUtleSwgTywgUCkge1xuICB2YXIgaGFzT3duID0gb3JkaW5hcnlIYXNPd25NZXRhZGF0YShNZXRhZGF0YUtleSwgTywgUCk7XG4gIGlmIChoYXNPd24pIHJldHVybiB0cnVlO1xuICB2YXIgcGFyZW50ID0gZ2V0UHJvdG90eXBlT2YoTyk7XG4gIHJldHVybiBwYXJlbnQgIT09IG51bGwgPyBvcmRpbmFyeUhhc01ldGFkYXRhKE1ldGFkYXRhS2V5LCBwYXJlbnQsIFApIDogZmFsc2U7XG59O1xuXG5tZXRhZGF0YS5leHAoeyBoYXNNZXRhZGF0YTogZnVuY3Rpb24gaGFzTWV0YWRhdGEobWV0YWRhdGFLZXksIHRhcmdldCAvKiAsIHRhcmdldEtleSAqLykge1xuICByZXR1cm4gb3JkaW5hcnlIYXNNZXRhZGF0YShtZXRhZGF0YUtleSwgYW5PYmplY3QodGFyZ2V0KSwgYXJndW1lbnRzLmxlbmd0aCA8IDMgPyB1bmRlZmluZWQgOiB0b01ldGFLZXkoYXJndW1lbnRzWzJdKSk7XG59IH0pO1xuIiwidmFyIG1ldGFkYXRhID0gcmVxdWlyZSgnLi9fbWV0YWRhdGEnKTtcbnZhciBhbk9iamVjdCA9IHJlcXVpcmUoJy4vX2FuLW9iamVjdCcpO1xudmFyIG9yZGluYXJ5SGFzT3duTWV0YWRhdGEgPSBtZXRhZGF0YS5oYXM7XG52YXIgdG9NZXRhS2V5ID0gbWV0YWRhdGEua2V5O1xuXG5tZXRhZGF0YS5leHAoeyBoYXNPd25NZXRhZGF0YTogZnVuY3Rpb24gaGFzT3duTWV0YWRhdGEobWV0YWRhdGFLZXksIHRhcmdldCAvKiAsIHRhcmdldEtleSAqLykge1xuICByZXR1cm4gb3JkaW5hcnlIYXNPd25NZXRhZGF0YShtZXRhZGF0YUtleSwgYW5PYmplY3QodGFyZ2V0KVxuICAgICwgYXJndW1lbnRzLmxlbmd0aCA8IDMgPyB1bmRlZmluZWQgOiB0b01ldGFLZXkoYXJndW1lbnRzWzJdKSk7XG59IH0pO1xuIiwidmFyICRtZXRhZGF0YSA9IHJlcXVpcmUoJy4vX21ldGFkYXRhJyk7XG52YXIgYW5PYmplY3QgPSByZXF1aXJlKCcuL19hbi1vYmplY3QnKTtcbnZhciBhRnVuY3Rpb24gPSByZXF1aXJlKCcuL19hLWZ1bmN0aW9uJyk7XG52YXIgdG9NZXRhS2V5ID0gJG1ldGFkYXRhLmtleTtcbnZhciBvcmRpbmFyeURlZmluZU93bk1ldGFkYXRhID0gJG1ldGFkYXRhLnNldDtcblxuJG1ldGFkYXRhLmV4cCh7IG1ldGFkYXRhOiBmdW5jdGlvbiBtZXRhZGF0YShtZXRhZGF0YUtleSwgbWV0YWRhdGFWYWx1ZSkge1xuICByZXR1cm4gZnVuY3Rpb24gZGVjb3JhdG9yKHRhcmdldCwgdGFyZ2V0S2V5KSB7XG4gICAgb3JkaW5hcnlEZWZpbmVPd25NZXRhZGF0YShcbiAgICAgIG1ldGFkYXRhS2V5LCBtZXRhZGF0YVZhbHVlLFxuICAgICAgKHRhcmdldEtleSAhPT0gdW5kZWZpbmVkID8gYW5PYmplY3QgOiBhRnVuY3Rpb24pKHRhcmdldCksXG4gICAgICB0b01ldGFLZXkodGFyZ2V0S2V5KVxuICAgICk7XG4gIH07XG59IH0pO1xuIiwiLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9wcm9wb3NhbC1zZXRtYXAtb2Zmcm9tLyNzZWMtc2V0LmZyb21cbnJlcXVpcmUoJy4vX3NldC1jb2xsZWN0aW9uLWZyb20nKSgnU2V0Jyk7XG4iLCIvLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL3Byb3Bvc2FsLXNldG1hcC1vZmZyb20vI3NlYy1zZXQub2ZcbnJlcXVpcmUoJy4vX3NldC1jb2xsZWN0aW9uLW9mJykoJ1NldCcpO1xuIiwiLy8gaHR0cHM6Ly9naXRodWIuY29tL0RhdmlkQnJ1YW50L01hcC1TZXQucHJvdG90eXBlLnRvSlNPTlxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LlIsICdTZXQnLCB7IHRvSlNPTjogcmVxdWlyZSgnLi9fY29sbGVjdGlvbi10by1qc29uJykoJ1NldCcpIH0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gaHR0cHM6Ly9naXRodWIuY29tL21hdGhpYXNieW5lbnMvU3RyaW5nLnByb3RvdHlwZS5hdFxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciAkYXQgPSByZXF1aXJlKCcuL19zdHJpbmctYXQnKSh0cnVlKTtcblxuJGV4cG9ydCgkZXhwb3J0LlAsICdTdHJpbmcnLCB7XG4gIGF0OiBmdW5jdGlvbiBhdChwb3MpIHtcbiAgICByZXR1cm4gJGF0KHRoaXMsIHBvcyk7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9TdHJpbmcucHJvdG90eXBlLm1hdGNoQWxsL1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciBkZWZpbmVkID0gcmVxdWlyZSgnLi9fZGVmaW5lZCcpO1xudmFyIHRvTGVuZ3RoID0gcmVxdWlyZSgnLi9fdG8tbGVuZ3RoJyk7XG52YXIgaXNSZWdFeHAgPSByZXF1aXJlKCcuL19pcy1yZWdleHAnKTtcbnZhciBnZXRGbGFncyA9IHJlcXVpcmUoJy4vX2ZsYWdzJyk7XG52YXIgUmVnRXhwUHJvdG8gPSBSZWdFeHAucHJvdG90eXBlO1xuXG52YXIgJFJlZ0V4cFN0cmluZ0l0ZXJhdG9yID0gZnVuY3Rpb24gKHJlZ2V4cCwgc3RyaW5nKSB7XG4gIHRoaXMuX3IgPSByZWdleHA7XG4gIHRoaXMuX3MgPSBzdHJpbmc7XG59O1xuXG5yZXF1aXJlKCcuL19pdGVyLWNyZWF0ZScpKCRSZWdFeHBTdHJpbmdJdGVyYXRvciwgJ1JlZ0V4cCBTdHJpbmcnLCBmdW5jdGlvbiBuZXh0KCkge1xuICB2YXIgbWF0Y2ggPSB0aGlzLl9yLmV4ZWModGhpcy5fcyk7XG4gIHJldHVybiB7IHZhbHVlOiBtYXRjaCwgZG9uZTogbWF0Y2ggPT09IG51bGwgfTtcbn0pO1xuXG4kZXhwb3J0KCRleHBvcnQuUCwgJ1N0cmluZycsIHtcbiAgbWF0Y2hBbGw6IGZ1bmN0aW9uIG1hdGNoQWxsKHJlZ2V4cCkge1xuICAgIGRlZmluZWQodGhpcyk7XG4gICAgaWYgKCFpc1JlZ0V4cChyZWdleHApKSB0aHJvdyBUeXBlRXJyb3IocmVnZXhwICsgJyBpcyBub3QgYSByZWdleHAhJyk7XG4gICAgdmFyIFMgPSBTdHJpbmcodGhpcyk7XG4gICAgdmFyIGZsYWdzID0gJ2ZsYWdzJyBpbiBSZWdFeHBQcm90byA/IFN0cmluZyhyZWdleHAuZmxhZ3MpIDogZ2V0RmxhZ3MuY2FsbChyZWdleHApO1xuICAgIHZhciByeCA9IG5ldyBSZWdFeHAocmVnZXhwLnNvdXJjZSwgfmZsYWdzLmluZGV4T2YoJ2cnKSA/IGZsYWdzIDogJ2cnICsgZmxhZ3MpO1xuICAgIHJ4Lmxhc3RJbmRleCA9IHRvTGVuZ3RoKHJlZ2V4cC5sYXN0SW5kZXgpO1xuICAgIHJldHVybiBuZXcgJFJlZ0V4cFN0cmluZ0l0ZXJhdG9yKHJ4LCBTKTtcbiAgfVxufSk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBodHRwczovL2dpdGh1Yi5jb20vdGMzOS9wcm9wb3NhbC1zdHJpbmctcGFkLXN0YXJ0LWVuZFxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciAkcGFkID0gcmVxdWlyZSgnLi9fc3RyaW5nLXBhZCcpO1xudmFyIHVzZXJBZ2VudCA9IHJlcXVpcmUoJy4vX3VzZXItYWdlbnQnKTtcblxuLy8gaHR0cHM6Ly9naXRodWIuY29tL3psb2lyb2NrL2NvcmUtanMvaXNzdWVzLzI4MFxuJGV4cG9ydCgkZXhwb3J0LlAgKyAkZXhwb3J0LkYgKiAvVmVyc2lvblxcLzEwXFwuXFxkKyhcXC5cXGQrKT8gU2FmYXJpXFwvLy50ZXN0KHVzZXJBZ2VudCksICdTdHJpbmcnLCB7XG4gIHBhZEVuZDogZnVuY3Rpb24gcGFkRW5kKG1heExlbmd0aCAvKiAsIGZpbGxTdHJpbmcgPSAnICcgKi8pIHtcbiAgICByZXR1cm4gJHBhZCh0aGlzLCBtYXhMZW5ndGgsIGFyZ3VtZW50cy5sZW5ndGggPiAxID8gYXJndW1lbnRzWzFdIDogdW5kZWZpbmVkLCBmYWxzZSk7XG4gIH1cbn0pO1xuIiwiJ3VzZSBzdHJpY3QnO1xuLy8gaHR0cHM6Ly9naXRodWIuY29tL3RjMzkvcHJvcG9zYWwtc3RyaW5nLXBhZC1zdGFydC1lbmRcbnZhciAkZXhwb3J0ID0gcmVxdWlyZSgnLi9fZXhwb3J0Jyk7XG52YXIgJHBhZCA9IHJlcXVpcmUoJy4vX3N0cmluZy1wYWQnKTtcbnZhciB1c2VyQWdlbnQgPSByZXF1aXJlKCcuL191c2VyLWFnZW50Jyk7XG5cbi8vIGh0dHBzOi8vZ2l0aHViLmNvbS96bG9pcm9jay9jb3JlLWpzL2lzc3Vlcy8yODBcbiRleHBvcnQoJGV4cG9ydC5QICsgJGV4cG9ydC5GICogL1ZlcnNpb25cXC8xMFxcLlxcZCsoXFwuXFxkKyk/IFNhZmFyaVxcLy8udGVzdCh1c2VyQWdlbnQpLCAnU3RyaW5nJywge1xuICBwYWRTdGFydDogZnVuY3Rpb24gcGFkU3RhcnQobWF4TGVuZ3RoIC8qICwgZmlsbFN0cmluZyA9ICcgJyAqLykge1xuICAgIHJldHVybiAkcGFkKHRoaXMsIG1heExlbmd0aCwgYXJndW1lbnRzLmxlbmd0aCA+IDEgPyBhcmd1bWVudHNbMV0gOiB1bmRlZmluZWQsIHRydWUpO1xuICB9XG59KTtcbiIsIid1c2Ugc3RyaWN0Jztcbi8vIGh0dHBzOi8vZ2l0aHViLmNvbS9zZWJtYXJrYmFnZS9lY21hc2NyaXB0LXN0cmluZy1sZWZ0LXJpZ2h0LXRyaW1cbnJlcXVpcmUoJy4vX3N0cmluZy10cmltJykoJ3RyaW1MZWZ0JywgZnVuY3Rpb24gKCR0cmltKSB7XG4gIHJldHVybiBmdW5jdGlvbiB0cmltTGVmdCgpIHtcbiAgICByZXR1cm4gJHRyaW0odGhpcywgMSk7XG4gIH07XG59LCAndHJpbVN0YXJ0Jyk7XG4iLCIndXNlIHN0cmljdCc7XG4vLyBodHRwczovL2dpdGh1Yi5jb20vc2VibWFya2JhZ2UvZWNtYXNjcmlwdC1zdHJpbmctbGVmdC1yaWdodC10cmltXG5yZXF1aXJlKCcuL19zdHJpbmctdHJpbScpKCd0cmltUmlnaHQnLCBmdW5jdGlvbiAoJHRyaW0pIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIHRyaW1SaWdodCgpIHtcbiAgICByZXR1cm4gJHRyaW0odGhpcywgMik7XG4gIH07XG59LCAndHJpbUVuZCcpO1xuIiwicmVxdWlyZSgnLi9fd2tzLWRlZmluZScpKCdhc3luY0l0ZXJhdG9yJyk7XG4iLCJyZXF1aXJlKCcuL193a3MtZGVmaW5lJykoJ29ic2VydmFibGUnKTtcbiIsIi8vIGh0dHBzOi8vZ2l0aHViLmNvbS90YzM5L3Byb3Bvc2FsLWdsb2JhbFxudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcblxuJGV4cG9ydCgkZXhwb3J0LlMsICdTeXN0ZW0nLCB7IGdsb2JhbDogcmVxdWlyZSgnLi9fZ2xvYmFsJykgfSk7XG4iLCIvLyBodHRwczovL3RjMzkuZ2l0aHViLmlvL3Byb3Bvc2FsLXNldG1hcC1vZmZyb20vI3NlYy13ZWFrbWFwLmZyb21cbnJlcXVpcmUoJy4vX3NldC1jb2xsZWN0aW9uLWZyb20nKSgnV2Vha01hcCcpO1xuIiwiLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9wcm9wb3NhbC1zZXRtYXAtb2Zmcm9tLyNzZWMtd2Vha21hcC5vZlxucmVxdWlyZSgnLi9fc2V0LWNvbGxlY3Rpb24tb2YnKSgnV2Vha01hcCcpO1xuIiwiLy8gaHR0cHM6Ly90YzM5LmdpdGh1Yi5pby9wcm9wb3NhbC1zZXRtYXAtb2Zmcm9tLyNzZWMtd2Vha3NldC5mcm9tXG5yZXF1aXJlKCcuL19zZXQtY29sbGVjdGlvbi1mcm9tJykoJ1dlYWtTZXQnKTtcbiIsIi8vIGh0dHBzOi8vdGMzOS5naXRodWIuaW8vcHJvcG9zYWwtc2V0bWFwLW9mZnJvbS8jc2VjLXdlYWtzZXQub2ZcbnJlcXVpcmUoJy4vX3NldC1jb2xsZWN0aW9uLW9mJykoJ1dlYWtTZXQnKTtcbiIsInZhciAkaXRlcmF0b3JzID0gcmVxdWlyZSgnLi9lczYuYXJyYXkuaXRlcmF0b3InKTtcbnZhciBnZXRLZXlzID0gcmVxdWlyZSgnLi9fb2JqZWN0LWtleXMnKTtcbnZhciByZWRlZmluZSA9IHJlcXVpcmUoJy4vX3JlZGVmaW5lJyk7XG52YXIgZ2xvYmFsID0gcmVxdWlyZSgnLi9fZ2xvYmFsJyk7XG52YXIgaGlkZSA9IHJlcXVpcmUoJy4vX2hpZGUnKTtcbnZhciBJdGVyYXRvcnMgPSByZXF1aXJlKCcuL19pdGVyYXRvcnMnKTtcbnZhciB3a3MgPSByZXF1aXJlKCcuL193a3MnKTtcbnZhciBJVEVSQVRPUiA9IHdrcygnaXRlcmF0b3InKTtcbnZhciBUT19TVFJJTkdfVEFHID0gd2tzKCd0b1N0cmluZ1RhZycpO1xudmFyIEFycmF5VmFsdWVzID0gSXRlcmF0b3JzLkFycmF5O1xuXG52YXIgRE9NSXRlcmFibGVzID0ge1xuICBDU1NSdWxlTGlzdDogdHJ1ZSwgLy8gVE9ETzogTm90IHNwZWMgY29tcGxpYW50LCBzaG91bGQgYmUgZmFsc2UuXG4gIENTU1N0eWxlRGVjbGFyYXRpb246IGZhbHNlLFxuICBDU1NWYWx1ZUxpc3Q6IGZhbHNlLFxuICBDbGllbnRSZWN0TGlzdDogZmFsc2UsXG4gIERPTVJlY3RMaXN0OiBmYWxzZSxcbiAgRE9NU3RyaW5nTGlzdDogZmFsc2UsXG4gIERPTVRva2VuTGlzdDogdHJ1ZSxcbiAgRGF0YVRyYW5zZmVySXRlbUxpc3Q6IGZhbHNlLFxuICBGaWxlTGlzdDogZmFsc2UsXG4gIEhUTUxBbGxDb2xsZWN0aW9uOiBmYWxzZSxcbiAgSFRNTENvbGxlY3Rpb246IGZhbHNlLFxuICBIVE1MRm9ybUVsZW1lbnQ6IGZhbHNlLFxuICBIVE1MU2VsZWN0RWxlbWVudDogZmFsc2UsXG4gIE1lZGlhTGlzdDogdHJ1ZSwgLy8gVE9ETzogTm90IHNwZWMgY29tcGxpYW50LCBzaG91bGQgYmUgZmFsc2UuXG4gIE1pbWVUeXBlQXJyYXk6IGZhbHNlLFxuICBOYW1lZE5vZGVNYXA6IGZhbHNlLFxuICBOb2RlTGlzdDogdHJ1ZSxcbiAgUGFpbnRSZXF1ZXN0TGlzdDogZmFsc2UsXG4gIFBsdWdpbjogZmFsc2UsXG4gIFBsdWdpbkFycmF5OiBmYWxzZSxcbiAgU1ZHTGVuZ3RoTGlzdDogZmFsc2UsXG4gIFNWR051bWJlckxpc3Q6IGZhbHNlLFxuICBTVkdQYXRoU2VnTGlzdDogZmFsc2UsXG4gIFNWR1BvaW50TGlzdDogZmFsc2UsXG4gIFNWR1N0cmluZ0xpc3Q6IGZhbHNlLFxuICBTVkdUcmFuc2Zvcm1MaXN0OiBmYWxzZSxcbiAgU291cmNlQnVmZmVyTGlzdDogZmFsc2UsXG4gIFN0eWxlU2hlZXRMaXN0OiB0cnVlLCAvLyBUT0RPOiBOb3Qgc3BlYyBjb21wbGlhbnQsIHNob3VsZCBiZSBmYWxzZS5cbiAgVGV4dFRyYWNrQ3VlTGlzdDogZmFsc2UsXG4gIFRleHRUcmFja0xpc3Q6IGZhbHNlLFxuICBUb3VjaExpc3Q6IGZhbHNlXG59O1xuXG5mb3IgKHZhciBjb2xsZWN0aW9ucyA9IGdldEtleXMoRE9NSXRlcmFibGVzKSwgaSA9IDA7IGkgPCBjb2xsZWN0aW9ucy5sZW5ndGg7IGkrKykge1xuICB2YXIgTkFNRSA9IGNvbGxlY3Rpb25zW2ldO1xuICB2YXIgZXhwbGljaXQgPSBET01JdGVyYWJsZXNbTkFNRV07XG4gIHZhciBDb2xsZWN0aW9uID0gZ2xvYmFsW05BTUVdO1xuICB2YXIgcHJvdG8gPSBDb2xsZWN0aW9uICYmIENvbGxlY3Rpb24ucHJvdG90eXBlO1xuICB2YXIga2V5O1xuICBpZiAocHJvdG8pIHtcbiAgICBpZiAoIXByb3RvW0lURVJBVE9SXSkgaGlkZShwcm90bywgSVRFUkFUT1IsIEFycmF5VmFsdWVzKTtcbiAgICBpZiAoIXByb3RvW1RPX1NUUklOR19UQUddKSBoaWRlKHByb3RvLCBUT19TVFJJTkdfVEFHLCBOQU1FKTtcbiAgICBJdGVyYXRvcnNbTkFNRV0gPSBBcnJheVZhbHVlcztcbiAgICBpZiAoZXhwbGljaXQpIGZvciAoa2V5IGluICRpdGVyYXRvcnMpIGlmICghcHJvdG9ba2V5XSkgcmVkZWZpbmUocHJvdG8sIGtleSwgJGl0ZXJhdG9yc1trZXldLCB0cnVlKTtcbiAgfVxufVxuIiwidmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciAkdGFzayA9IHJlcXVpcmUoJy4vX3Rhc2snKTtcbiRleHBvcnQoJGV4cG9ydC5HICsgJGV4cG9ydC5CLCB7XG4gIHNldEltbWVkaWF0ZTogJHRhc2suc2V0LFxuICBjbGVhckltbWVkaWF0ZTogJHRhc2suY2xlYXJcbn0pO1xuIiwiLy8gaWU5LSBzZXRUaW1lb3V0ICYgc2V0SW50ZXJ2YWwgYWRkaXRpb25hbCBwYXJhbWV0ZXJzIGZpeFxudmFyIGdsb2JhbCA9IHJlcXVpcmUoJy4vX2dsb2JhbCcpO1xudmFyICRleHBvcnQgPSByZXF1aXJlKCcuL19leHBvcnQnKTtcbnZhciB1c2VyQWdlbnQgPSByZXF1aXJlKCcuL191c2VyLWFnZW50Jyk7XG52YXIgc2xpY2UgPSBbXS5zbGljZTtcbnZhciBNU0lFID0gL01TSUUgLlxcLi8udGVzdCh1c2VyQWdlbnQpOyAvLyA8LSBkaXJ0eSBpZTktIGNoZWNrXG52YXIgd3JhcCA9IGZ1bmN0aW9uIChzZXQpIHtcbiAgcmV0dXJuIGZ1bmN0aW9uIChmbiwgdGltZSAvKiAsIC4uLmFyZ3MgKi8pIHtcbiAgICB2YXIgYm91bmRBcmdzID0gYXJndW1lbnRzLmxlbmd0aCA+IDI7XG4gICAgdmFyIGFyZ3MgPSBib3VuZEFyZ3MgPyBzbGljZS5jYWxsKGFyZ3VtZW50cywgMikgOiBmYWxzZTtcbiAgICByZXR1cm4gc2V0KGJvdW5kQXJncyA/IGZ1bmN0aW9uICgpIHtcbiAgICAgIC8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBuby1uZXctZnVuY1xuICAgICAgKHR5cGVvZiBmbiA9PSAnZnVuY3Rpb24nID8gZm4gOiBGdW5jdGlvbihmbikpLmFwcGx5KHRoaXMsIGFyZ3MpO1xuICAgIH0gOiBmbiwgdGltZSk7XG4gIH07XG59O1xuJGV4cG9ydCgkZXhwb3J0LkcgKyAkZXhwb3J0LkIgKyAkZXhwb3J0LkYgKiBNU0lFLCB7XG4gIHNldFRpbWVvdXQ6IHdyYXAoZ2xvYmFsLnNldFRpbWVvdXQpLFxuICBzZXRJbnRlcnZhbDogd3JhcChnbG9iYWwuc2V0SW50ZXJ2YWwpXG59KTtcbiIsInJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3ltYm9sJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm9iamVjdC5jcmVhdGUnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYub2JqZWN0LmRlZmluZS1wcm9wZXJ0eScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5vYmplY3QuZGVmaW5lLXByb3BlcnRpZXMnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYub2JqZWN0LmdldC1vd24tcHJvcGVydHktZGVzY3JpcHRvcicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5vYmplY3QuZ2V0LXByb3RvdHlwZS1vZicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5vYmplY3Qua2V5cycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5vYmplY3QuZ2V0LW93bi1wcm9wZXJ0eS1uYW1lcycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5vYmplY3QuZnJlZXplJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm9iamVjdC5zZWFsJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm9iamVjdC5wcmV2ZW50LWV4dGVuc2lvbnMnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYub2JqZWN0LmlzLWZyb3plbicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5vYmplY3QuaXMtc2VhbGVkJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm9iamVjdC5pcy1leHRlbnNpYmxlJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm9iamVjdC5hc3NpZ24nKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYub2JqZWN0LmlzJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm9iamVjdC5zZXQtcHJvdG90eXBlLW9mJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm9iamVjdC50by1zdHJpbmcnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuZnVuY3Rpb24uYmluZCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5mdW5jdGlvbi5uYW1lJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmZ1bmN0aW9uLmhhcy1pbnN0YW5jZScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5wYXJzZS1pbnQnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYucGFyc2UtZmxvYXQnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubnVtYmVyLmNvbnN0cnVjdG9yJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm51bWJlci50by1maXhlZCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5udW1iZXIudG8tcHJlY2lzaW9uJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm51bWJlci5lcHNpbG9uJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm51bWJlci5pcy1maW5pdGUnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubnVtYmVyLmlzLWludGVnZXInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubnVtYmVyLmlzLW5hbicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5udW1iZXIuaXMtc2FmZS1pbnRlZ2VyJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm51bWJlci5tYXgtc2FmZS1pbnRlZ2VyJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm51bWJlci5taW4tc2FmZS1pbnRlZ2VyJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm51bWJlci5wYXJzZS1mbG9hdCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5udW1iZXIucGFyc2UtaW50Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm1hdGguYWNvc2gnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubWF0aC5hc2luaCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5tYXRoLmF0YW5oJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm1hdGguY2JydCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5tYXRoLmNsejMyJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm1hdGguY29zaCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5tYXRoLmV4cG0xJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm1hdGguZnJvdW5kJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm1hdGguaHlwb3QnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubWF0aC5pbXVsJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm1hdGgubG9nMTAnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubWF0aC5sb2cxcCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5tYXRoLmxvZzInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubWF0aC5zaWduJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2Lm1hdGguc2luaCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5tYXRoLnRhbmgnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubWF0aC50cnVuYycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5zdHJpbmcuZnJvbS1jb2RlLXBvaW50Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5yYXcnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLnRyaW0nKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLml0ZXJhdG9yJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5jb2RlLXBvaW50LWF0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5lbmRzLXdpdGgnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLmluY2x1ZGVzJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5yZXBlYXQnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLnN0YXJ0cy13aXRoJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5hbmNob3InKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLmJpZycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5zdHJpbmcuYmxpbmsnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLmJvbGQnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLmZpeGVkJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5mb250Y29sb3InKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLmZvbnRzaXplJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5pdGFsaWNzJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5saW5rJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5zbWFsbCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5zdHJpbmcuc3RyaWtlJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnN0cmluZy5zdWInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuc3RyaW5nLnN1cCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5kYXRlLm5vdycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5kYXRlLnRvLWpzb24nKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuZGF0ZS50by1pc28tc3RyaW5nJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmRhdGUudG8tc3RyaW5nJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmRhdGUudG8tcHJpbWl0aXZlJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmFycmF5LmlzLWFycmF5Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmFycmF5LmZyb20nKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuYXJyYXkub2YnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuYXJyYXkuam9pbicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5hcnJheS5zbGljZScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5hcnJheS5zb3J0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmFycmF5LmZvci1lYWNoJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmFycmF5Lm1hcCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5hcnJheS5maWx0ZXInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuYXJyYXkuc29tZScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5hcnJheS5ldmVyeScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5hcnJheS5yZWR1Y2UnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuYXJyYXkucmVkdWNlLXJpZ2h0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmFycmF5LmluZGV4LW9mJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmFycmF5Lmxhc3QtaW5kZXgtb2YnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuYXJyYXkuY29weS13aXRoaW4nKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuYXJyYXkuZmlsbCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5hcnJheS5maW5kJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LmFycmF5LmZpbmQtaW5kZXgnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYuYXJyYXkuc3BlY2llcycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5hcnJheS5pdGVyYXRvcicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5yZWdleHAuY29uc3RydWN0b3InKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYucmVnZXhwLmV4ZWMnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYucmVnZXhwLnRvLXN0cmluZycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5yZWdleHAuZmxhZ3MnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYucmVnZXhwLm1hdGNoJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZ2V4cC5yZXBsYWNlJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZ2V4cC5zZWFyY2gnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYucmVnZXhwLnNwbGl0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnByb21pc2UnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYubWFwJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnNldCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi53ZWFrLW1hcCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi53ZWFrLXNldCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi50eXBlZC5hcnJheS1idWZmZXInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYudHlwZWQuZGF0YS12aWV3Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnR5cGVkLmludDgtYXJyYXknKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYudHlwZWQudWludDgtYXJyYXknKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYudHlwZWQudWludDgtY2xhbXBlZC1hcnJheScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi50eXBlZC5pbnQxNi1hcnJheScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi50eXBlZC51aW50MTYtYXJyYXknKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYudHlwZWQuaW50MzItYXJyYXknKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYudHlwZWQudWludDMyLWFycmF5Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnR5cGVkLmZsb2F0MzItYXJyYXknKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYudHlwZWQuZmxvYXQ2NC1hcnJheScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5yZWZsZWN0LmFwcGx5Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZmxlY3QuY29uc3RydWN0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZmxlY3QuZGVmaW5lLXByb3BlcnR5Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZmxlY3QuZGVsZXRlLXByb3BlcnR5Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZmxlY3QuZW51bWVyYXRlJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZmxlY3QuZ2V0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZmxlY3QuZ2V0LW93bi1wcm9wZXJ0eS1kZXNjcmlwdG9yJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM2LnJlZmxlY3QuZ2V0LXByb3RvdHlwZS1vZicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5yZWZsZWN0LmhhcycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5yZWZsZWN0LmlzLWV4dGVuc2libGUnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczYucmVmbGVjdC5vd24ta2V5cycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5yZWZsZWN0LnByZXZlbnQtZXh0ZW5zaW9ucycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5yZWZsZWN0LnNldCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNi5yZWZsZWN0LnNldC1wcm90b3R5cGUtb2YnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcuYXJyYXkuaW5jbHVkZXMnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcuYXJyYXkuZmxhdC1tYXAnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcuYXJyYXkuZmxhdHRlbicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5zdHJpbmcuYXQnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcuc3RyaW5nLnBhZC1zdGFydCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5zdHJpbmcucGFkLWVuZCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5zdHJpbmcudHJpbS1sZWZ0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3LnN0cmluZy50cmltLXJpZ2h0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3LnN0cmluZy5tYXRjaC1hbGwnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcuc3ltYm9sLmFzeW5jLWl0ZXJhdG9yJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3LnN5bWJvbC5vYnNlcnZhYmxlJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3Lm9iamVjdC5nZXQtb3duLXByb3BlcnR5LWRlc2NyaXB0b3JzJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3Lm9iamVjdC52YWx1ZXMnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcub2JqZWN0LmVudHJpZXMnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcub2JqZWN0LmRlZmluZS1nZXR0ZXInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcub2JqZWN0LmRlZmluZS1zZXR0ZXInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcub2JqZWN0Lmxvb2t1cC1nZXR0ZXInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcub2JqZWN0Lmxvb2t1cC1zZXR0ZXInKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcubWFwLnRvLWpzb24nKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcuc2V0LnRvLWpzb24nKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcubWFwLm9mJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3LnNldC5vZicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy53ZWFrLW1hcC5vZicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy53ZWFrLXNldC5vZicpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5tYXAuZnJvbScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5zZXQuZnJvbScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy53ZWFrLW1hcC5mcm9tJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3LndlYWstc2V0LmZyb20nKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcuZ2xvYmFsJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3LnN5c3RlbS5nbG9iYWwnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcuZXJyb3IuaXMtZXJyb3InKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcubWF0aC5jbGFtcCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5tYXRoLmRlZy1wZXItcmFkJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3Lm1hdGguZGVncmVlcycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5tYXRoLmZzY2FsZScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5tYXRoLmlhZGRoJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3Lm1hdGguaXN1YmgnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcubWF0aC5pbXVsaCcpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5tYXRoLnJhZC1wZXItZGVnJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3Lm1hdGgucmFkaWFucycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5tYXRoLnNjYWxlJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3Lm1hdGgudW11bGgnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcubWF0aC5zaWduYml0Jyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3LnByb21pc2UuZmluYWxseScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5wcm9taXNlLnRyeScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5yZWZsZWN0LmRlZmluZS1tZXRhZGF0YScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5yZWZsZWN0LmRlbGV0ZS1tZXRhZGF0YScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5yZWZsZWN0LmdldC1tZXRhZGF0YScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5yZWZsZWN0LmdldC1tZXRhZGF0YS1rZXlzJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3LnJlZmxlY3QuZ2V0LW93bi1tZXRhZGF0YScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5yZWZsZWN0LmdldC1vd24tbWV0YWRhdGEta2V5cycpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5yZWZsZWN0Lmhhcy1tZXRhZGF0YScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5yZWZsZWN0Lmhhcy1vd24tbWV0YWRhdGEnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy9lczcucmVmbGVjdC5tZXRhZGF0YScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL2VzNy5hc2FwJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvZXM3Lm9ic2VydmFibGUnKTtcbnJlcXVpcmUoJy4vbW9kdWxlcy93ZWIudGltZXJzJyk7XG5yZXF1aXJlKCcuL21vZHVsZXMvd2ViLmltbWVkaWF0ZScpO1xucmVxdWlyZSgnLi9tb2R1bGVzL3dlYi5kb20uaXRlcmFibGUnKTtcbm1vZHVsZS5leHBvcnRzID0gcmVxdWlyZSgnLi9tb2R1bGVzL19jb3JlJyk7XG4iLCJleHBvcnRzLnJlYWQgPSBmdW5jdGlvbiAoYnVmZmVyLCBvZmZzZXQsIGlzTEUsIG1MZW4sIG5CeXRlcykge1xuICB2YXIgZSwgbVxuICB2YXIgZUxlbiA9IChuQnl0ZXMgKiA4KSAtIG1MZW4gLSAxXG4gIHZhciBlTWF4ID0gKDEgPDwgZUxlbikgLSAxXG4gIHZhciBlQmlhcyA9IGVNYXggPj4gMVxuICB2YXIgbkJpdHMgPSAtN1xuICB2YXIgaSA9IGlzTEUgPyAobkJ5dGVzIC0gMSkgOiAwXG4gIHZhciBkID0gaXNMRSA/IC0xIDogMVxuICB2YXIgcyA9IGJ1ZmZlcltvZmZzZXQgKyBpXVxuXG4gIGkgKz0gZFxuXG4gIGUgPSBzICYgKCgxIDw8ICgtbkJpdHMpKSAtIDEpXG4gIHMgPj49ICgtbkJpdHMpXG4gIG5CaXRzICs9IGVMZW5cbiAgZm9yICg7IG5CaXRzID4gMDsgZSA9IChlICogMjU2KSArIGJ1ZmZlcltvZmZzZXQgKyBpXSwgaSArPSBkLCBuQml0cyAtPSA4KSB7fVxuXG4gIG0gPSBlICYgKCgxIDw8ICgtbkJpdHMpKSAtIDEpXG4gIGUgPj49ICgtbkJpdHMpXG4gIG5CaXRzICs9IG1MZW5cbiAgZm9yICg7IG5CaXRzID4gMDsgbSA9IChtICogMjU2KSArIGJ1ZmZlcltvZmZzZXQgKyBpXSwgaSArPSBkLCBuQml0cyAtPSA4KSB7fVxuXG4gIGlmIChlID09PSAwKSB7XG4gICAgZSA9IDEgLSBlQmlhc1xuICB9IGVsc2UgaWYgKGUgPT09IGVNYXgpIHtcbiAgICByZXR1cm4gbSA/IE5hTiA6ICgocyA/IC0xIDogMSkgKiBJbmZpbml0eSlcbiAgfSBlbHNlIHtcbiAgICBtID0gbSArIE1hdGgucG93KDIsIG1MZW4pXG4gICAgZSA9IGUgLSBlQmlhc1xuICB9XG4gIHJldHVybiAocyA/IC0xIDogMSkgKiBtICogTWF0aC5wb3coMiwgZSAtIG1MZW4pXG59XG5cbmV4cG9ydHMud3JpdGUgPSBmdW5jdGlvbiAoYnVmZmVyLCB2YWx1ZSwgb2Zmc2V0LCBpc0xFLCBtTGVuLCBuQnl0ZXMpIHtcbiAgdmFyIGUsIG0sIGNcbiAgdmFyIGVMZW4gPSAobkJ5dGVzICogOCkgLSBtTGVuIC0gMVxuICB2YXIgZU1heCA9ICgxIDw8IGVMZW4pIC0gMVxuICB2YXIgZUJpYXMgPSBlTWF4ID4+IDFcbiAgdmFyIHJ0ID0gKG1MZW4gPT09IDIzID8gTWF0aC5wb3coMiwgLTI0KSAtIE1hdGgucG93KDIsIC03NykgOiAwKVxuICB2YXIgaSA9IGlzTEUgPyAwIDogKG5CeXRlcyAtIDEpXG4gIHZhciBkID0gaXNMRSA/IDEgOiAtMVxuICB2YXIgcyA9IHZhbHVlIDwgMCB8fCAodmFsdWUgPT09IDAgJiYgMSAvIHZhbHVlIDwgMCkgPyAxIDogMFxuXG4gIHZhbHVlID0gTWF0aC5hYnModmFsdWUpXG5cbiAgaWYgKGlzTmFOKHZhbHVlKSB8fCB2YWx1ZSA9PT0gSW5maW5pdHkpIHtcbiAgICBtID0gaXNOYU4odmFsdWUpID8gMSA6IDBcbiAgICBlID0gZU1heFxuICB9IGVsc2Uge1xuICAgIGUgPSBNYXRoLmZsb29yKE1hdGgubG9nKHZhbHVlKSAvIE1hdGguTE4yKVxuICAgIGlmICh2YWx1ZSAqIChjID0gTWF0aC5wb3coMiwgLWUpKSA8IDEpIHtcbiAgICAgIGUtLVxuICAgICAgYyAqPSAyXG4gICAgfVxuICAgIGlmIChlICsgZUJpYXMgPj0gMSkge1xuICAgICAgdmFsdWUgKz0gcnQgLyBjXG4gICAgfSBlbHNlIHtcbiAgICAgIHZhbHVlICs9IHJ0ICogTWF0aC5wb3coMiwgMSAtIGVCaWFzKVxuICAgIH1cbiAgICBpZiAodmFsdWUgKiBjID49IDIpIHtcbiAgICAgIGUrK1xuICAgICAgYyAvPSAyXG4gICAgfVxuXG4gICAgaWYgKGUgKyBlQmlhcyA+PSBlTWF4KSB7XG4gICAgICBtID0gMFxuICAgICAgZSA9IGVNYXhcbiAgICB9IGVsc2UgaWYgKGUgKyBlQmlhcyA+PSAxKSB7XG4gICAgICBtID0gKCh2YWx1ZSAqIGMpIC0gMSkgKiBNYXRoLnBvdygyLCBtTGVuKVxuICAgICAgZSA9IGUgKyBlQmlhc1xuICAgIH0gZWxzZSB7XG4gICAgICBtID0gdmFsdWUgKiBNYXRoLnBvdygyLCBlQmlhcyAtIDEpICogTWF0aC5wb3coMiwgbUxlbilcbiAgICAgIGUgPSAwXG4gICAgfVxuICB9XG5cbiAgZm9yICg7IG1MZW4gPj0gODsgYnVmZmVyW29mZnNldCArIGldID0gbSAmIDB4ZmYsIGkgKz0gZCwgbSAvPSAyNTYsIG1MZW4gLT0gOCkge31cblxuICBlID0gKGUgPDwgbUxlbikgfCBtXG4gIGVMZW4gKz0gbUxlblxuICBmb3IgKDsgZUxlbiA+IDA7IGJ1ZmZlcltvZmZzZXQgKyBpXSA9IGUgJiAweGZmLCBpICs9IGQsIGUgLz0gMjU2LCBlTGVuIC09IDgpIHt9XG5cbiAgYnVmZmVyW29mZnNldCArIGkgLSBkXSB8PSBzICogMTI4XG59XG4iLCIvKipcbiAqIENvbnZlcnQgYXJyYXkgb2YgMTYgYnl0ZSB2YWx1ZXMgdG8gVVVJRCBzdHJpbmcgZm9ybWF0IG9mIHRoZSBmb3JtOlxuICogWFhYWFhYWFgtWFhYWC1YWFhYLVhYWFgtWFhYWFhYWFhYWFhYXG4gKi9cbnZhciBieXRlVG9IZXggPSBbXTtcbmZvciAodmFyIGkgPSAwOyBpIDwgMjU2OyArK2kpIHtcbiAgYnl0ZVRvSGV4W2ldID0gKGkgKyAweDEwMCkudG9TdHJpbmcoMTYpLnN1YnN0cigxKTtcbn1cblxuZnVuY3Rpb24gYnl0ZXNUb1V1aWQoYnVmLCBvZmZzZXQpIHtcbiAgdmFyIGkgPSBvZmZzZXQgfHwgMDtcbiAgdmFyIGJ0aCA9IGJ5dGVUb0hleDtcbiAgLy8gam9pbiB1c2VkIHRvIGZpeCBtZW1vcnkgaXNzdWUgY2F1c2VkIGJ5IGNvbmNhdGVuYXRpb246IGh0dHBzOi8vYnVncy5jaHJvbWl1bS5vcmcvcC92OC9pc3N1ZXMvZGV0YWlsP2lkPTMxNzUjYzRcbiAgcmV0dXJuIChbYnRoW2J1ZltpKytdXSwgYnRoW2J1ZltpKytdXSwgXG5cdGJ0aFtidWZbaSsrXV0sIGJ0aFtidWZbaSsrXV0sICctJyxcblx0YnRoW2J1ZltpKytdXSwgYnRoW2J1ZltpKytdXSwgJy0nLFxuXHRidGhbYnVmW2krK11dLCBidGhbYnVmW2krK11dLCAnLScsXG5cdGJ0aFtidWZbaSsrXV0sIGJ0aFtidWZbaSsrXV0sICctJyxcblx0YnRoW2J1ZltpKytdXSwgYnRoW2J1ZltpKytdXSxcblx0YnRoW2J1ZltpKytdXSwgYnRoW2J1ZltpKytdXSxcblx0YnRoW2J1ZltpKytdXSwgYnRoW2J1ZltpKytdXV0pLmpvaW4oJycpO1xufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGJ5dGVzVG9VdWlkO1xuIiwiLy8gVW5pcXVlIElEIGNyZWF0aW9uIHJlcXVpcmVzIGEgaGlnaCBxdWFsaXR5IHJhbmRvbSAjIGdlbmVyYXRvci4gIEluIHRoZVxuLy8gYnJvd3NlciB0aGlzIGlzIGEgbGl0dGxlIGNvbXBsaWNhdGVkIGR1ZSB0byB1bmtub3duIHF1YWxpdHkgb2YgTWF0aC5yYW5kb20oKVxuLy8gYW5kIGluY29uc2lzdGVudCBzdXBwb3J0IGZvciB0aGUgYGNyeXB0b2AgQVBJLiAgV2UgZG8gdGhlIGJlc3Qgd2UgY2FuIHZpYVxuLy8gZmVhdHVyZS1kZXRlY3Rpb25cblxuLy8gZ2V0UmFuZG9tVmFsdWVzIG5lZWRzIHRvIGJlIGludm9rZWQgaW4gYSBjb250ZXh0IHdoZXJlIFwidGhpc1wiIGlzIGEgQ3J5cHRvXG4vLyBpbXBsZW1lbnRhdGlvbi4gQWxzbywgZmluZCB0aGUgY29tcGxldGUgaW1wbGVtZW50YXRpb24gb2YgY3J5cHRvIG9uIElFMTEuXG52YXIgZ2V0UmFuZG9tVmFsdWVzID0gKHR5cGVvZihjcnlwdG8pICE9ICd1bmRlZmluZWQnICYmIGNyeXB0by5nZXRSYW5kb21WYWx1ZXMgJiYgY3J5cHRvLmdldFJhbmRvbVZhbHVlcy5iaW5kKGNyeXB0bykpIHx8XG4gICAgICAgICAgICAgICAgICAgICAgKHR5cGVvZihtc0NyeXB0bykgIT0gJ3VuZGVmaW5lZCcgJiYgdHlwZW9mIHdpbmRvdy5tc0NyeXB0by5nZXRSYW5kb21WYWx1ZXMgPT0gJ2Z1bmN0aW9uJyAmJiBtc0NyeXB0by5nZXRSYW5kb21WYWx1ZXMuYmluZChtc0NyeXB0bykpO1xuXG5pZiAoZ2V0UmFuZG9tVmFsdWVzKSB7XG4gIC8vIFdIQVRXRyBjcnlwdG8gUk5HIC0gaHR0cDovL3dpa2kud2hhdHdnLm9yZy93aWtpL0NyeXB0b1xuICB2YXIgcm5kczggPSBuZXcgVWludDhBcnJheSgxNik7IC8vIGVzbGludC1kaXNhYmxlLWxpbmUgbm8tdW5kZWZcblxuICBtb2R1bGUuZXhwb3J0cyA9IGZ1bmN0aW9uIHdoYXR3Z1JORygpIHtcbiAgICBnZXRSYW5kb21WYWx1ZXMocm5kczgpO1xuICAgIHJldHVybiBybmRzODtcbiAgfTtcbn0gZWxzZSB7XG4gIC8vIE1hdGgucmFuZG9tKCktYmFzZWQgKFJORylcbiAgLy9cbiAgLy8gSWYgYWxsIGVsc2UgZmFpbHMsIHVzZSBNYXRoLnJhbmRvbSgpLiAgSXQncyBmYXN0LCBidXQgaXMgb2YgdW5zcGVjaWZpZWRcbiAgLy8gcXVhbGl0eS5cbiAgdmFyIHJuZHMgPSBuZXcgQXJyYXkoMTYpO1xuXG4gIG1vZHVsZS5leHBvcnRzID0gZnVuY3Rpb24gbWF0aFJORygpIHtcbiAgICBmb3IgKHZhciBpID0gMCwgcjsgaSA8IDE2OyBpKyspIHtcbiAgICAgIGlmICgoaSAmIDB4MDMpID09PSAwKSByID0gTWF0aC5yYW5kb20oKSAqIDB4MTAwMDAwMDAwO1xuICAgICAgcm5kc1tpXSA9IHIgPj4+ICgoaSAmIDB4MDMpIDw8IDMpICYgMHhmZjtcbiAgICB9XG5cbiAgICByZXR1cm4gcm5kcztcbiAgfTtcbn1cbiIsInZhciBybmcgPSByZXF1aXJlKCcuL2xpYi9ybmcnKTtcbnZhciBieXRlc1RvVXVpZCA9IHJlcXVpcmUoJy4vbGliL2J5dGVzVG9VdWlkJyk7XG5cbmZ1bmN0aW9uIHY0KG9wdGlvbnMsIGJ1Ziwgb2Zmc2V0KSB7XG4gIHZhciBpID0gYnVmICYmIG9mZnNldCB8fCAwO1xuXG4gIGlmICh0eXBlb2Yob3B0aW9ucykgPT0gJ3N0cmluZycpIHtcbiAgICBidWYgPSBvcHRpb25zID09PSAnYmluYXJ5JyA/IG5ldyBBcnJheSgxNikgOiBudWxsO1xuICAgIG9wdGlvbnMgPSBudWxsO1xuICB9XG4gIG9wdGlvbnMgPSBvcHRpb25zIHx8IHt9O1xuXG4gIHZhciBybmRzID0gb3B0aW9ucy5yYW5kb20gfHwgKG9wdGlvbnMucm5nIHx8IHJuZykoKTtcblxuICAvLyBQZXIgNC40LCBzZXQgYml0cyBmb3IgdmVyc2lvbiBhbmQgYGNsb2NrX3NlcV9oaV9hbmRfcmVzZXJ2ZWRgXG4gIHJuZHNbNl0gPSAocm5kc1s2XSAmIDB4MGYpIHwgMHg0MDtcbiAgcm5kc1s4XSA9IChybmRzWzhdICYgMHgzZikgfCAweDgwO1xuXG4gIC8vIENvcHkgYnl0ZXMgdG8gYnVmZmVyLCBpZiBwcm92aWRlZFxuICBpZiAoYnVmKSB7XG4gICAgZm9yICh2YXIgaWkgPSAwOyBpaSA8IDE2OyArK2lpKSB7XG4gICAgICBidWZbaSArIGlpXSA9IHJuZHNbaWldO1xuICAgIH1cbiAgfVxuXG4gIHJldHVybiBidWYgfHwgYnl0ZXNUb1V1aWQocm5kcyk7XG59XG5cbm1vZHVsZS5leHBvcnRzID0gdjQ7XG4iLCJ2YXIgZztcblxuLy8gVGhpcyB3b3JrcyBpbiBub24tc3RyaWN0IG1vZGVcbmcgPSAoZnVuY3Rpb24oKSB7XG5cdHJldHVybiB0aGlzO1xufSkoKTtcblxudHJ5IHtcblx0Ly8gVGhpcyB3b3JrcyBpZiBldmFsIGlzIGFsbG93ZWQgKHNlZSBDU1ApXG5cdGcgPSBnIHx8IG5ldyBGdW5jdGlvbihcInJldHVybiB0aGlzXCIpKCk7XG59IGNhdGNoIChlKSB7XG5cdC8vIFRoaXMgd29ya3MgaWYgdGhlIHdpbmRvdyByZWZlcmVuY2UgaXMgYXZhaWxhYmxlXG5cdGlmICh0eXBlb2Ygd2luZG93ID09PSBcIm9iamVjdFwiKSBnID0gd2luZG93O1xufVxuXG4vLyBnIGNhbiBzdGlsbCBiZSB1bmRlZmluZWQsIGJ1dCBub3RoaW5nIHRvIGRvIGFib3V0IGl0Li4uXG4vLyBXZSByZXR1cm4gdW5kZWZpbmVkLCBpbnN0ZWFkIG9mIG5vdGhpbmcgaGVyZSwgc28gaXQnc1xuLy8gZWFzaWVyIHRvIGhhbmRsZSB0aGlzIGNhc2UuIGlmKCFnbG9iYWwpIHsgLi4ufVxuXG5tb2R1bGUuZXhwb3J0cyA9IGc7XG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcbmltcG9ydCB7IFRpbWVyIH0gZnJvbSAnLi9UaW1lci5qcyc7XHJcblxyXG5jb25zdCBEZWZhdWx0QWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWUgPSA2MDsgLy8gc2Vjb25kc1xyXG5cclxuZXhwb3J0IGNsYXNzIEFjY2Vzc1Rva2VuRXZlbnRzIHtcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcih7XHJcbiAgICAgICAgYWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWUgPSBEZWZhdWx0QWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWUsXHJcbiAgICAgICAgYWNjZXNzVG9rZW5FeHBpcmluZ1RpbWVyID0gbmV3IFRpbWVyKFwiQWNjZXNzIHRva2VuIGV4cGlyaW5nXCIpLFxyXG4gICAgICAgIGFjY2Vzc1Rva2VuRXhwaXJlZFRpbWVyID0gbmV3IFRpbWVyKFwiQWNjZXNzIHRva2VuIGV4cGlyZWRcIilcclxuICAgIH0gPSB7fSkge1xyXG4gICAgICAgIHRoaXMuX2FjY2Vzc1Rva2VuRXhwaXJpbmdOb3RpZmljYXRpb25UaW1lID0gYWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWU7XHJcblxyXG4gICAgICAgIHRoaXMuX2FjY2Vzc1Rva2VuRXhwaXJpbmcgPSBhY2Nlc3NUb2tlbkV4cGlyaW5nVGltZXI7XHJcbiAgICAgICAgdGhpcy5fYWNjZXNzVG9rZW5FeHBpcmVkID0gYWNjZXNzVG9rZW5FeHBpcmVkVGltZXI7XHJcbiAgICB9XHJcblxyXG4gICAgbG9hZChjb250YWluZXIpIHtcclxuICAgICAgICAvLyBvbmx5IHJlZ2lzdGVyIGV2ZW50cyBpZiB0aGVyZSdzIGFuIGFjY2VzcyB0b2tlbiBhbmQgaXQgaGFzIGFuIGV4cGlyYXRpb25cclxuICAgICAgICBpZiAoY29udGFpbmVyLmFjY2Vzc190b2tlbiAmJiBjb250YWluZXIuZXhwaXJlc19pbiAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICAgIGxldCBkdXJhdGlvbiA9IGNvbnRhaW5lci5leHBpcmVzX2luO1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJBY2Nlc3NUb2tlbkV2ZW50cy5sb2FkOiBhY2Nlc3MgdG9rZW4gcHJlc2VudCwgcmVtYWluaW5nIGR1cmF0aW9uOlwiLCBkdXJhdGlvbik7XHJcblxyXG4gICAgICAgICAgICBpZiAoZHVyYXRpb24gPiAwKSB7XHJcbiAgICAgICAgICAgICAgICAvLyBvbmx5IHJlZ2lzdGVyIGV4cGlyaW5nIGlmIHdlIHN0aWxsIGhhdmUgdGltZVxyXG4gICAgICAgICAgICAgICAgbGV0IGV4cGlyaW5nID0gZHVyYXRpb24gLSB0aGlzLl9hY2Nlc3NUb2tlbkV4cGlyaW5nTm90aWZpY2F0aW9uVGltZTtcclxuICAgICAgICAgICAgICAgIGlmIChleHBpcmluZyA8PSAwKXtcclxuICAgICAgICAgICAgICAgICAgICBleHBpcmluZyA9IDE7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIkFjY2Vzc1Rva2VuRXZlbnRzLmxvYWQ6IHJlZ2lzdGVyaW5nIGV4cGlyaW5nIHRpbWVyIGluOlwiLCBleHBpcmluZyk7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9hY2Nlc3NUb2tlbkV4cGlyaW5nLmluaXQoZXhwaXJpbmcpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiQWNjZXNzVG9rZW5FdmVudHMubG9hZDogY2FuY2VsaW5nIGV4aXN0aW5nIGV4cGlyaW5nIHRpbWVyIGJlY2FzZSB3ZSdyZSBwYXN0IGV4cGlyYXRpb24uXCIpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fYWNjZXNzVG9rZW5FeHBpcmluZy5jYW5jZWwoKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgLy8gaWYgaXQncyBuZWdhdGl2ZSwgaXQgd2lsbCBzdGlsbCBmaXJlXHJcbiAgICAgICAgICAgIGxldCBleHBpcmVkID0gZHVyYXRpb24gKyAxO1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJBY2Nlc3NUb2tlbkV2ZW50cy5sb2FkOiByZWdpc3RlcmluZyBleHBpcmVkIHRpbWVyIGluOlwiLCBleHBpcmVkKTtcclxuICAgICAgICAgICAgdGhpcy5fYWNjZXNzVG9rZW5FeHBpcmVkLmluaXQoZXhwaXJlZCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICB0aGlzLl9hY2Nlc3NUb2tlbkV4cGlyaW5nLmNhbmNlbCgpO1xyXG4gICAgICAgICAgICB0aGlzLl9hY2Nlc3NUb2tlbkV4cGlyZWQuY2FuY2VsKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHVubG9hZCgpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJBY2Nlc3NUb2tlbkV2ZW50cy51bmxvYWQ6IGNhbmNlbGluZyBleGlzdGluZyBhY2Nlc3MgdG9rZW4gdGltZXJzXCIpO1xyXG4gICAgICAgIHRoaXMuX2FjY2Vzc1Rva2VuRXhwaXJpbmcuY2FuY2VsKCk7XHJcbiAgICAgICAgdGhpcy5fYWNjZXNzVG9rZW5FeHBpcmVkLmNhbmNlbCgpO1xyXG4gICAgfVxyXG5cclxuICAgIGFkZEFjY2Vzc1Rva2VuRXhwaXJpbmcoY2IpIHtcclxuICAgICAgICB0aGlzLl9hY2Nlc3NUb2tlbkV4cGlyaW5nLmFkZEhhbmRsZXIoY2IpO1xyXG4gICAgfVxyXG4gICAgcmVtb3ZlQWNjZXNzVG9rZW5FeHBpcmluZyhjYikge1xyXG4gICAgICAgIHRoaXMuX2FjY2Vzc1Rva2VuRXhwaXJpbmcucmVtb3ZlSGFuZGxlcihjYik7XHJcbiAgICB9XHJcblxyXG4gICAgYWRkQWNjZXNzVG9rZW5FeHBpcmVkKGNiKSB7XHJcbiAgICAgICAgdGhpcy5fYWNjZXNzVG9rZW5FeHBpcmVkLmFkZEhhbmRsZXIoY2IpO1xyXG4gICAgfVxyXG4gICAgcmVtb3ZlQWNjZXNzVG9rZW5FeHBpcmVkKGNiKSB7XHJcbiAgICAgICAgdGhpcy5fYWNjZXNzVG9rZW5FeHBpcmVkLnJlbW92ZUhhbmRsZXIoY2IpO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuXHJcbmNvbnN0IERlZmF1bHRJbnRlcnZhbCA9IDIwMDA7XHJcblxyXG5leHBvcnQgY2xhc3MgQ2hlY2tTZXNzaW9uSUZyYW1lIHtcclxuICAgIGNvbnN0cnVjdG9yKGNhbGxiYWNrLCBjbGllbnRfaWQsIHVybCwgaW50ZXJ2YWwsIHN0b3BPbkVycm9yID0gdHJ1ZSkge1xyXG4gICAgICAgIHRoaXMuX2NhbGxiYWNrID0gY2FsbGJhY2s7XHJcbiAgICAgICAgdGhpcy5fY2xpZW50X2lkID0gY2xpZW50X2lkO1xyXG4gICAgICAgIHRoaXMuX3VybCA9IHVybDtcclxuICAgICAgICB0aGlzLl9pbnRlcnZhbCA9IGludGVydmFsIHx8IERlZmF1bHRJbnRlcnZhbDtcclxuICAgICAgICB0aGlzLl9zdG9wT25FcnJvciA9IHN0b3BPbkVycm9yO1xyXG5cclxuICAgICAgICB2YXIgaWR4ID0gdXJsLmluZGV4T2YoXCIvXCIsIHVybC5pbmRleE9mKFwiLy9cIikgKyAyKTtcclxuICAgICAgICB0aGlzLl9mcmFtZV9vcmlnaW4gPSB1cmwuc3Vic3RyKDAsIGlkeCk7XHJcblxyXG4gICAgICAgIHRoaXMuX2ZyYW1lID0gd2luZG93LmRvY3VtZW50LmNyZWF0ZUVsZW1lbnQoXCJpZnJhbWVcIik7XHJcblxyXG4gICAgICAgIC8vIHNob3RndW4gYXBwcm9hY2hcclxuICAgICAgICB0aGlzLl9mcmFtZS5zdHlsZS52aXNpYmlsaXR5ID0gXCJoaWRkZW5cIjtcclxuICAgICAgICB0aGlzLl9mcmFtZS5zdHlsZS5wb3NpdGlvbiA9IFwiYWJzb2x1dGVcIjtcclxuICAgICAgICB0aGlzLl9mcmFtZS5zdHlsZS5kaXNwbGF5ID0gXCJub25lXCI7XHJcbiAgICAgICAgdGhpcy5fZnJhbWUuc3R5bGUud2lkdGggPSAwO1xyXG4gICAgICAgIHRoaXMuX2ZyYW1lLnN0eWxlLmhlaWdodCA9IDA7XHJcblxyXG4gICAgICAgIHRoaXMuX2ZyYW1lLnNyYyA9IHVybDtcclxuICAgIH1cclxuICAgIGxvYWQoKSB7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlKSA9PiB7XHJcbiAgICAgICAgICAgIHRoaXMuX2ZyYW1lLm9ubG9hZCA9ICgpID0+IHtcclxuICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgd2luZG93LmRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQodGhpcy5fZnJhbWUpO1xyXG4gICAgICAgICAgICB0aGlzLl9ib3VuZE1lc3NhZ2VFdmVudCA9IHRoaXMuX21lc3NhZ2UuYmluZCh0aGlzKTtcclxuICAgICAgICAgICAgd2luZG93LmFkZEV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIHRoaXMuX2JvdW5kTWVzc2FnZUV2ZW50LCBmYWxzZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBfbWVzc2FnZShlKSB7XHJcbiAgICAgICAgaWYgKGUub3JpZ2luID09PSB0aGlzLl9mcmFtZV9vcmlnaW4gJiZcclxuICAgICAgICAgICAgZS5zb3VyY2UgPT09IHRoaXMuX2ZyYW1lLmNvbnRlbnRXaW5kb3dcclxuICAgICAgICApIHtcclxuICAgICAgICAgICAgaWYgKGUuZGF0YSA9PT0gXCJlcnJvclwiKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJDaGVja1Nlc3Npb25JRnJhbWU6IGVycm9yIG1lc3NhZ2UgZnJvbSBjaGVjayBzZXNzaW9uIG9wIGlmcmFtZVwiKTtcclxuICAgICAgICAgICAgICAgIGlmICh0aGlzLl9zdG9wT25FcnJvcikge1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuc3RvcCgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2UgaWYgKGUuZGF0YSA9PT0gXCJjaGFuZ2VkXCIpIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIkNoZWNrU2Vzc2lvbklGcmFtZTogY2hhbmdlZCBtZXNzYWdlIGZyb20gY2hlY2sgc2Vzc2lvbiBvcCBpZnJhbWVcIik7XHJcbiAgICAgICAgICAgICAgICB0aGlzLnN0b3AoKTtcclxuICAgICAgICAgICAgICAgIHRoaXMuX2NhbGxiYWNrKCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJDaGVja1Nlc3Npb25JRnJhbWU6IFwiICsgZS5kYXRhICsgXCIgbWVzc2FnZSBmcm9tIGNoZWNrIHNlc3Npb24gb3AgaWZyYW1lXCIpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgc3RhcnQoc2Vzc2lvbl9zdGF0ZSkge1xyXG4gICAgICAgIGlmICh0aGlzLl9zZXNzaW9uX3N0YXRlICE9PSBzZXNzaW9uX3N0YXRlKSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIkNoZWNrU2Vzc2lvbklGcmFtZS5zdGFydFwiKTtcclxuXHJcbiAgICAgICAgICAgIHRoaXMuc3RvcCgpO1xyXG5cclxuICAgICAgICAgICAgdGhpcy5fc2Vzc2lvbl9zdGF0ZSA9IHNlc3Npb25fc3RhdGU7XHJcblxyXG4gICAgICAgICAgICBsZXQgc2VuZCA9ICgpID0+IHtcclxuICAgICAgICAgICAgICAgIHRoaXMuX2ZyYW1lLmNvbnRlbnRXaW5kb3cucG9zdE1lc3NhZ2UodGhpcy5fY2xpZW50X2lkICsgXCIgXCIgKyB0aGlzLl9zZXNzaW9uX3N0YXRlLCB0aGlzLl9mcmFtZV9vcmlnaW4pO1xyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICBcclxuICAgICAgICAgICAgLy8gdHJpZ2dlciBub3dcclxuICAgICAgICAgICAgc2VuZCgpO1xyXG5cclxuICAgICAgICAgICAgLy8gYW5kIHNldHVwIHRpbWVyXHJcbiAgICAgICAgICAgIHRoaXMuX3RpbWVyID0gd2luZG93LnNldEludGVydmFsKHNlbmQsIHRoaXMuX2ludGVydmFsKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgc3RvcCgpIHtcclxuICAgICAgICB0aGlzLl9zZXNzaW9uX3N0YXRlID0gbnVsbDtcclxuXHJcbiAgICAgICAgaWYgKHRoaXMuX3RpbWVyKSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIkNoZWNrU2Vzc2lvbklGcmFtZS5zdG9wXCIpO1xyXG5cclxuICAgICAgICAgICAgd2luZG93LmNsZWFySW50ZXJ2YWwodGhpcy5fdGltZXIpO1xyXG4gICAgICAgICAgICB0aGlzLl90aW1lciA9IG51bGw7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IENvcmRvdmFQb3B1cFdpbmRvdyB9IGZyb20gJy4vQ29yZG92YVBvcHVwV2luZG93LmpzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBDb3Jkb3ZhSUZyYW1lTmF2aWdhdG9yIHtcclxuXHJcbiAgICBwcmVwYXJlKHBhcmFtcykge1xyXG4gICAgICAgIHBhcmFtcy5wb3B1cFdpbmRvd0ZlYXR1cmVzID0gJ2hpZGRlbj15ZXMnO1xyXG4gICAgICAgIGxldCBwb3B1cCA9IG5ldyBDb3Jkb3ZhUG9wdXBXaW5kb3cocGFyYW1zKTtcclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHBvcHVwKTtcclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBDb3Jkb3ZhUG9wdXBXaW5kb3cgfSBmcm9tICcuL0NvcmRvdmFQb3B1cFdpbmRvdy5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgQ29yZG92YVBvcHVwTmF2aWdhdG9yIHtcclxuXHJcbiAgICBwcmVwYXJlKHBhcmFtcykge1xyXG4gICAgICAgIGxldCBwb3B1cCA9IG5ldyBDb3Jkb3ZhUG9wdXBXaW5kb3cocGFyYW1zKTtcclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHBvcHVwKTtcclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcblxyXG5jb25zdCBEZWZhdWx0UG9wdXBGZWF0dXJlcyA9ICdsb2NhdGlvbj1ubyx0b29sYmFyPW5vLHpvb209bm8nO1xyXG5jb25zdCBEZWZhdWx0UG9wdXBUYXJnZXQgPSBcIl9ibGFua1wiO1xyXG5cclxuZXhwb3J0IGNsYXNzIENvcmRvdmFQb3B1cFdpbmRvdyB7XHJcblxyXG4gICAgY29uc3RydWN0b3IocGFyYW1zKSB7XHJcbiAgICAgICAgdGhpcy5fcHJvbWlzZSA9IG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuICAgICAgICAgICAgdGhpcy5fcmVzb2x2ZSA9IHJlc29sdmU7XHJcbiAgICAgICAgICAgIHRoaXMuX3JlamVjdCA9IHJlamVjdDtcclxuICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgdGhpcy5mZWF0dXJlcyA9IHBhcmFtcy5wb3B1cFdpbmRvd0ZlYXR1cmVzIHx8IERlZmF1bHRQb3B1cEZlYXR1cmVzO1xyXG4gICAgICAgIHRoaXMudGFyZ2V0ID0gcGFyYW1zLnBvcHVwV2luZG93VGFyZ2V0IHx8IERlZmF1bHRQb3B1cFRhcmdldDtcclxuICAgICAgICBcclxuICAgICAgICB0aGlzLnJlZGlyZWN0X3VyaSA9IHBhcmFtcy5zdGFydFVybDtcclxuICAgICAgICBMb2cuZGVidWcoXCJDb3Jkb3ZhUG9wdXBXaW5kb3cuY3RvcjogcmVkaXJlY3RfdXJpOiBcIiArIHRoaXMucmVkaXJlY3RfdXJpKTtcclxuICAgIH1cclxuXHJcbiAgICBfaXNJbkFwcEJyb3dzZXJJbnN0YWxsZWQoY29yZG92YU1ldGFkYXRhKSB7XHJcbiAgICAgICAgcmV0dXJuIFtcImNvcmRvdmEtcGx1Z2luLWluYXBwYnJvd3NlclwiLCBcImNvcmRvdmEtcGx1Z2luLWluYXBwYnJvd3Nlci5pbmFwcGJyb3dzZXJcIiwgXCJvcmcuYXBhY2hlLmNvcmRvdmEuaW5hcHBicm93c2VyXCJdLnNvbWUoZnVuY3Rpb24gKG5hbWUpIHtcclxuICAgICAgICAgICAgcmV0dXJuIGNvcmRvdmFNZXRhZGF0YS5oYXNPd25Qcm9wZXJ0eShuYW1lKVxyXG4gICAgICAgIH0pXHJcbiAgICB9XHJcbiAgICBcclxuICAgIG5hdmlnYXRlKHBhcmFtcykge1xyXG4gICAgICAgIGlmICghcGFyYW1zIHx8ICFwYXJhbXMudXJsKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2Vycm9yKFwiTm8gdXJsIHByb3ZpZGVkXCIpO1xyXG4gICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgIGlmICghd2luZG93LmNvcmRvdmEpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl9lcnJvcihcImNvcmRvdmEgaXMgdW5kZWZpbmVkXCIpXHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgXHJcbiAgICAgICAgICAgIHZhciBjb3Jkb3ZhTWV0YWRhdGEgPSB3aW5kb3cuY29yZG92YS5yZXF1aXJlKFwiY29yZG92YS9wbHVnaW5fbGlzdFwiKS5tZXRhZGF0YTtcclxuICAgICAgICAgICAgaWYgKHRoaXMuX2lzSW5BcHBCcm93c2VySW5zdGFsbGVkKGNvcmRvdmFNZXRhZGF0YSkgPT09IGZhbHNlKSB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fZXJyb3IoXCJJbkFwcEJyb3dzZXIgcGx1Z2luIG5vdCBmb3VuZFwiKVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHRoaXMuX3BvcHVwID0gY29yZG92YS5JbkFwcEJyb3dzZXIub3BlbihwYXJhbXMudXJsLCB0aGlzLnRhcmdldCwgdGhpcy5mZWF0dXJlcyk7XHJcbiAgICAgICAgICAgIGlmICh0aGlzLl9wb3B1cCkge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiQ29yZG92YVBvcHVwV2luZG93Lm5hdmlnYXRlOiBwb3B1cCBzdWNjZXNzZnVsbHkgY3JlYXRlZFwiKTtcclxuICAgICAgICAgICAgICAgIFxyXG4gICAgICAgICAgICAgICAgdGhpcy5fZXhpdENhbGxiYWNrRXZlbnQgPSB0aGlzLl9leGl0Q2FsbGJhY2suYmluZCh0aGlzKTsgXHJcbiAgICAgICAgICAgICAgICB0aGlzLl9sb2FkU3RhcnRDYWxsYmFja0V2ZW50ID0gdGhpcy5fbG9hZFN0YXJ0Q2FsbGJhY2suYmluZCh0aGlzKTtcclxuICAgICAgICAgICAgICAgIFxyXG4gICAgICAgICAgICAgICAgdGhpcy5fcG9wdXAuYWRkRXZlbnRMaXN0ZW5lcihcImV4aXRcIiwgdGhpcy5fZXhpdENhbGxiYWNrRXZlbnQsIGZhbHNlKTtcclxuICAgICAgICAgICAgICAgIHRoaXMuX3BvcHVwLmFkZEV2ZW50TGlzdGVuZXIoXCJsb2Fkc3RhcnRcIiwgdGhpcy5fbG9hZFN0YXJ0Q2FsbGJhY2tFdmVudCwgZmFsc2UpO1xyXG4gICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fZXJyb3IoXCJFcnJvciBvcGVuaW5nIHBvcHVwIHdpbmRvd1wiKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICByZXR1cm4gdGhpcy5wcm9taXNlO1xyXG4gICAgfVxyXG5cclxuICAgIGdldCBwcm9taXNlKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9wcm9taXNlO1xyXG4gICAgfVxyXG5cclxuICAgIF9sb2FkU3RhcnRDYWxsYmFjayhldmVudCkge1xyXG4gICAgICAgIGlmIChldmVudC51cmwuaW5kZXhPZih0aGlzLnJlZGlyZWN0X3VyaSkgPT09IDApIHtcclxuICAgICAgICAgICAgdGhpcy5fc3VjY2Vzcyh7IHVybDogZXZlbnQudXJsIH0pO1xyXG4gICAgICAgIH0gICAgXHJcbiAgICB9XHJcbiAgICBfZXhpdENhbGxiYWNrKG1lc3NhZ2UpIHtcclxuICAgICAgICB0aGlzLl9lcnJvcihtZXNzYWdlKTsgICAgXHJcbiAgICB9XHJcbiAgICBcclxuICAgIF9zdWNjZXNzKGRhdGEpIHtcclxuICAgICAgICB0aGlzLl9jbGVhbnVwKCk7XHJcblxyXG4gICAgICAgIExvZy5kZWJ1ZyhcIkNvcmRvdmFQb3B1cFdpbmRvdzogU3VjY2Vzc2Z1bCByZXNwb25zZSBmcm9tIGNvcmRvdmEgcG9wdXAgd2luZG93XCIpO1xyXG4gICAgICAgIHRoaXMuX3Jlc29sdmUoZGF0YSk7XHJcbiAgICB9XHJcbiAgICBfZXJyb3IobWVzc2FnZSkge1xyXG4gICAgICAgIHRoaXMuX2NsZWFudXAoKTtcclxuXHJcbiAgICAgICAgTG9nLmVycm9yKG1lc3NhZ2UpO1xyXG4gICAgICAgIHRoaXMuX3JlamVjdChuZXcgRXJyb3IobWVzc2FnZSkpO1xyXG4gICAgfVxyXG5cclxuICAgIGNsb3NlKCkge1xyXG4gICAgICAgIHRoaXMuX2NsZWFudXAoKTtcclxuICAgIH1cclxuXHJcbiAgICBfY2xlYW51cCgpIHtcclxuICAgICAgICBpZiAodGhpcy5fcG9wdXApe1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJDb3Jkb3ZhUG9wdXBXaW5kb3c6IGNsZWFuaW5nIHVwIHBvcHVwXCIpO1xyXG4gICAgICAgICAgICB0aGlzLl9wb3B1cC5yZW1vdmVFdmVudExpc3RlbmVyKFwiZXhpdFwiLCB0aGlzLl9leGl0Q2FsbGJhY2tFdmVudCwgZmFsc2UpO1xyXG4gICAgICAgICAgICB0aGlzLl9wb3B1cC5yZW1vdmVFdmVudExpc3RlbmVyKFwibG9hZHN0YXJ0XCIsIHRoaXMuX2xvYWRTdGFydENhbGxiYWNrRXZlbnQsIGZhbHNlKTtcclxuICAgICAgICAgICAgdGhpcy5fcG9wdXAuY2xvc2UoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdGhpcy5fcG9wdXAgPSBudWxsO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBFcnJvclJlc3BvbnNlIGV4dGVuZHMgRXJyb3Ige1xyXG4gICAgY29uc3RydWN0b3Ioe2Vycm9yLCBlcnJvcl9kZXNjcmlwdGlvbiwgZXJyb3JfdXJpLCBzdGF0ZSwgc2Vzc2lvbl9zdGF0ZX09e31cclxuICAgICkge1xyXG4gICAgICAgICBpZiAoIWVycm9yKXtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiTm8gZXJyb3IgcGFzc2VkIHRvIEVycm9yUmVzcG9uc2VcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcImVycm9yXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgc3VwZXIoZXJyb3JfZGVzY3JpcHRpb24gfHwgZXJyb3IpO1xyXG5cclxuICAgICAgICB0aGlzLm5hbWUgPSBcIkVycm9yUmVzcG9uc2VcIjtcclxuXHJcbiAgICAgICAgdGhpcy5lcnJvciA9IGVycm9yO1xyXG4gICAgICAgIHRoaXMuZXJyb3JfZGVzY3JpcHRpb24gPSBlcnJvcl9kZXNjcmlwdGlvbjtcclxuICAgICAgICB0aGlzLmVycm9yX3VyaSA9IGVycm9yX3VyaTtcclxuXHJcbiAgICAgICAgdGhpcy5zdGF0ZSA9IHN0YXRlO1xyXG4gICAgICAgIHRoaXMuc2Vzc2lvbl9zdGF0ZSA9IHNlc3Npb25fc3RhdGU7XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5cclxuZXhwb3J0IGNsYXNzIEV2ZW50IHtcclxuXHJcbiAgICBjb25zdHJ1Y3RvcihuYW1lKSB7XHJcbiAgICAgICAgdGhpcy5fbmFtZSA9IG5hbWU7XHJcbiAgICAgICAgdGhpcy5fY2FsbGJhY2tzID0gW107XHJcbiAgICB9XHJcblxyXG4gICAgYWRkSGFuZGxlcihjYikge1xyXG4gICAgICAgIHRoaXMuX2NhbGxiYWNrcy5wdXNoKGNiKTtcclxuICAgIH1cclxuXHJcbiAgICByZW1vdmVIYW5kbGVyKGNiKSB7XHJcbiAgICAgICAgdmFyIGlkeCA9IHRoaXMuX2NhbGxiYWNrcy5maW5kSW5kZXgoaXRlbSA9PiBpdGVtID09PSBjYik7XHJcbiAgICAgICAgaWYgKGlkeCA+PSAwKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NhbGxiYWNrcy5zcGxpY2UoaWR4LCAxKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgcmFpc2UoLi4ucGFyYW1zKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiRXZlbnQ6IFJhaXNpbmcgZXZlbnQ6IFwiICsgdGhpcy5fbmFtZSk7XHJcbiAgICAgICAgZm9yIChsZXQgaSA9IDA7IGkgPCB0aGlzLl9jYWxsYmFja3MubGVuZ3RoOyBpKyspIHtcclxuICAgICAgICAgICAgdGhpcy5fY2FsbGJhY2tzW2ldKC4uLnBhcmFtcyk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmNvbnN0IHRpbWVyID0ge1xyXG4gICAgc2V0SW50ZXJ2YWw6IGZ1bmN0aW9uIChjYiwgZHVyYXRpb24pIHtcclxuICAgICAgICByZXR1cm4gc2V0SW50ZXJ2YWwoY2IsIGR1cmF0aW9uKTtcclxuICAgIH0sXHJcbiAgICBjbGVhckludGVydmFsOiBmdW5jdGlvbiAoaGFuZGxlKSB7XHJcbiAgICAgICAgcmV0dXJuIGNsZWFySW50ZXJ2YWwoaGFuZGxlKTtcclxuICAgIH1cclxufTtcclxuXHJcbmxldCB0ZXN0aW5nID0gZmFsc2U7XHJcbmxldCByZXF1ZXN0ID0gbnVsbDtcclxuXHJcbmV4cG9ydCBjbGFzcyBHbG9iYWwge1xyXG5cclxuICAgIHN0YXRpYyBfdGVzdGluZygpIHtcclxuICAgICAgICB0ZXN0aW5nID0gdHJ1ZTtcclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWMgZ2V0IGxvY2F0aW9uKCkge1xyXG4gICAgICAgIGlmICghdGVzdGluZykge1xyXG4gICAgICAgICAgICByZXR1cm4gbG9jYXRpb247XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHN0YXRpYyBnZXQgbG9jYWxTdG9yYWdlKCkge1xyXG4gICAgICAgIGlmICghdGVzdGluZyAmJiB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgICAgICByZXR1cm4gbG9jYWxTdG9yYWdlO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWMgZ2V0IHNlc3Npb25TdG9yYWdlKCkge1xyXG4gICAgICAgIGlmICghdGVzdGluZyAmJiB0eXBlb2Ygd2luZG93ICE9PSAndW5kZWZpbmVkJykge1xyXG4gICAgICAgICAgICByZXR1cm4gc2Vzc2lvblN0b3JhZ2U7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHN0YXRpYyBzZXRYTUxIdHRwUmVxdWVzdChuZXdSZXF1ZXN0KSB7XHJcbiAgICAgICAgcmVxdWVzdCA9IG5ld1JlcXVlc3Q7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhdGljIGdldCBYTUxIdHRwUmVxdWVzdCgpIHtcclxuICAgICAgICBpZiAoIXRlc3RpbmcgJiYgdHlwZW9mIHdpbmRvdyAhPT0gJ3VuZGVmaW5lZCcpIHtcclxuICAgICAgICAgICAgcmV0dXJuIHJlcXVlc3QgfHwgWE1MSHR0cFJlcXVlc3Q7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHN0YXRpYyBnZXQgdGltZXIoKSB7XHJcbiAgICAgICAgaWYgKCF0ZXN0aW5nKSB7XHJcbiAgICAgICAgICAgIHJldHVybiB0aW1lcjtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5pbXBvcnQgeyBJRnJhbWVXaW5kb3cgfSBmcm9tICcuL0lGcmFtZVdpbmRvdy5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgSUZyYW1lTmF2aWdhdG9yIHtcclxuXHJcbiAgICBwcmVwYXJlKHBhcmFtcykge1xyXG4gICAgICAgIGxldCBmcmFtZSA9IG5ldyBJRnJhbWVXaW5kb3cocGFyYW1zKTtcclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGZyYW1lKTtcclxuICAgIH1cclxuXHJcbiAgICBjYWxsYmFjayh1cmwpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJJRnJhbWVOYXZpZ2F0b3IuY2FsbGJhY2tcIik7XHJcblxyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIElGcmFtZVdpbmRvdy5ub3RpZnlQYXJlbnQodXJsKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZSgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjYXRjaCAoZSkge1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoZSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuXHJcbmNvbnN0IERlZmF1bHRUaW1lb3V0ID0gMTAwMDA7XHJcblxyXG5leHBvcnQgY2xhc3MgSUZyYW1lV2luZG93IHtcclxuXHJcbiAgICBjb25zdHJ1Y3RvcihwYXJhbXMpIHtcclxuICAgICAgICB0aGlzLl9wcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLl9yZXNvbHZlID0gcmVzb2x2ZTtcclxuICAgICAgICAgICAgdGhpcy5fcmVqZWN0ID0gcmVqZWN0O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICB0aGlzLl9ib3VuZE1lc3NhZ2VFdmVudCA9IHRoaXMuX21lc3NhZ2UuYmluZCh0aGlzKTtcclxuICAgICAgICB3aW5kb3cuYWRkRXZlbnRMaXN0ZW5lcihcIm1lc3NhZ2VcIiwgdGhpcy5fYm91bmRNZXNzYWdlRXZlbnQsIGZhbHNlKTtcclxuXHJcbiAgICAgICAgdGhpcy5fZnJhbWUgPSB3aW5kb3cuZG9jdW1lbnQuY3JlYXRlRWxlbWVudChcImlmcmFtZVwiKTtcclxuXHJcbiAgICAgICAgLy8gc2hvdGd1biBhcHByb2FjaFxyXG4gICAgICAgIHRoaXMuX2ZyYW1lLnN0eWxlLnZpc2liaWxpdHkgPSBcImhpZGRlblwiO1xyXG4gICAgICAgIHRoaXMuX2ZyYW1lLnN0eWxlLnBvc2l0aW9uID0gXCJhYnNvbHV0ZVwiO1xyXG4gICAgICAgIHRoaXMuX2ZyYW1lLnN0eWxlLmRpc3BsYXkgPSBcIm5vbmVcIjtcclxuICAgICAgICB0aGlzLl9mcmFtZS5zdHlsZS53aWR0aCA9IDA7XHJcbiAgICAgICAgdGhpcy5fZnJhbWUuc3R5bGUuaGVpZ2h0ID0gMDtcclxuXHJcbiAgICAgICAgd2luZG93LmRvY3VtZW50LmJvZHkuYXBwZW5kQ2hpbGQodGhpcy5fZnJhbWUpO1xyXG4gICAgfVxyXG5cclxuICAgIG5hdmlnYXRlKHBhcmFtcykge1xyXG4gICAgICAgIGlmICghcGFyYW1zIHx8ICFwYXJhbXMudXJsKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2Vycm9yKFwiTm8gdXJsIHByb3ZpZGVkXCIpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgbGV0IHRpbWVvdXQgPSBwYXJhbXMuc2lsZW50UmVxdWVzdFRpbWVvdXQgfHwgRGVmYXVsdFRpbWVvdXQ7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIklGcmFtZVdpbmRvdy5uYXZpZ2F0ZTogVXNpbmcgdGltZW91dCBvZjpcIiwgdGltZW91dCk7XHJcbiAgICAgICAgICAgIHRoaXMuX3RpbWVyID0gd2luZG93LnNldFRpbWVvdXQodGhpcy5fdGltZW91dC5iaW5kKHRoaXMpLCB0aW1lb3V0KTtcclxuICAgICAgICAgICAgdGhpcy5fZnJhbWUuc3JjID0gcGFyYW1zLnVybDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLnByb21pc2U7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IHByb21pc2UoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Byb21pc2U7XHJcbiAgICB9XHJcblxyXG4gICAgX3N1Y2Nlc3MoZGF0YSkge1xyXG4gICAgICAgIHRoaXMuX2NsZWFudXAoKTtcclxuXHJcbiAgICAgICAgTG9nLmRlYnVnKFwiSUZyYW1lV2luZG93OiBTdWNjZXNzZnVsIHJlc3BvbnNlIGZyb20gZnJhbWUgd2luZG93XCIpO1xyXG4gICAgICAgIHRoaXMuX3Jlc29sdmUoZGF0YSk7XHJcbiAgICB9XHJcbiAgICBfZXJyb3IobWVzc2FnZSkge1xyXG4gICAgICAgIHRoaXMuX2NsZWFudXAoKTtcclxuXHJcbiAgICAgICAgTG9nLmVycm9yKG1lc3NhZ2UpO1xyXG4gICAgICAgIHRoaXMuX3JlamVjdChuZXcgRXJyb3IobWVzc2FnZSkpO1xyXG4gICAgfVxyXG5cclxuICAgIGNsb3NlKCkge1xyXG4gICAgICAgIHRoaXMuX2NsZWFudXAoKTtcclxuICAgIH1cclxuXHJcbiAgICBfY2xlYW51cCgpIHtcclxuICAgICAgICBpZiAodGhpcy5fZnJhbWUpIHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiSUZyYW1lV2luZG93OiBjbGVhbnVwXCIpO1xyXG5cclxuICAgICAgICAgICAgd2luZG93LnJlbW92ZUV2ZW50TGlzdGVuZXIoXCJtZXNzYWdlXCIsIHRoaXMuX2JvdW5kTWVzc2FnZUV2ZW50LCBmYWxzZSk7XHJcbiAgICAgICAgICAgIHdpbmRvdy5jbGVhclRpbWVvdXQodGhpcy5fdGltZXIpO1xyXG4gICAgICAgICAgICB3aW5kb3cuZG9jdW1lbnQuYm9keS5yZW1vdmVDaGlsZCh0aGlzLl9mcmFtZSk7XHJcblxyXG4gICAgICAgICAgICB0aGlzLl90aW1lciA9IG51bGw7XHJcbiAgICAgICAgICAgIHRoaXMuX2ZyYW1lID0gbnVsbDtcclxuICAgICAgICAgICAgdGhpcy5fYm91bmRNZXNzYWdlRXZlbnQgPSBudWxsO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBfdGltZW91dCgpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJJRnJhbWVXaW5kb3cudGltZW91dFwiKTtcclxuICAgICAgICB0aGlzLl9lcnJvcihcIkZyYW1lIHdpbmRvdyB0aW1lZCBvdXRcIik7XHJcbiAgICB9XHJcblxyXG4gICAgX21lc3NhZ2UoZSkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIklGcmFtZVdpbmRvdy5tZXNzYWdlXCIpO1xyXG5cclxuICAgICAgICBpZiAodGhpcy5fdGltZXIgJiZcclxuICAgICAgICAgICAgZS5vcmlnaW4gPT09IHRoaXMuX29yaWdpbiAmJlxyXG4gICAgICAgICAgICBlLnNvdXJjZSA9PT0gdGhpcy5fZnJhbWUuY29udGVudFdpbmRvd1xyXG4gICAgICAgICkge1xyXG4gICAgICAgICAgICBsZXQgdXJsID0gZS5kYXRhO1xyXG4gICAgICAgICAgICBpZiAodXJsKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9zdWNjZXNzKHsgdXJsOiB1cmwgfSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9lcnJvcihcIkludmFsaWQgcmVzcG9uc2UgZnJvbSBmcmFtZVwiKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBnZXQgX29yaWdpbigpIHtcclxuICAgICAgICByZXR1cm4gbG9jYXRpb24ucHJvdG9jb2wgKyBcIi8vXCIgKyBsb2NhdGlvbi5ob3N0O1xyXG4gICAgfVxyXG5cclxuICAgIHN0YXRpYyBub3RpZnlQYXJlbnQodXJsKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiSUZyYW1lV2luZG93Lm5vdGlmeVBhcmVudFwiKTtcclxuICAgICAgICBpZiAod2luZG93LmZyYW1lRWxlbWVudCkge1xyXG4gICAgICAgICAgICB1cmwgPSB1cmwgfHwgd2luZG93LmxvY2F0aW9uLmhyZWY7XHJcbiAgICAgICAgICAgIGlmICh1cmwpIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIklGcmFtZVdpbmRvdy5ub3RpZnlQYXJlbnQ6IHBvc3RpbmcgdXJsIG1lc3NhZ2UgdG8gcGFyZW50XCIpO1xyXG4gICAgICAgICAgICAgICAgd2luZG93LnBhcmVudC5wb3N0TWVzc2FnZSh1cmwsIGxvY2F0aW9uLnByb3RvY29sICsgXCIvL1wiICsgbG9jYXRpb24uaG9zdCk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5cclxuZXhwb3J0IGNsYXNzIEluTWVtb3J5V2ViU3RvcmFnZXtcclxuICAgIGNvbnN0cnVjdG9yKCl7XHJcbiAgICAgICAgdGhpcy5fZGF0YSA9IHt9O1xyXG4gICAgfVxyXG5cclxuICAgIGdldEl0ZW0oa2V5KSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiSW5NZW1vcnlXZWJTdG9yYWdlLmdldEl0ZW1cIiwga2V5KTtcclxuICAgICAgICByZXR1cm4gdGhpcy5fZGF0YVtrZXldO1xyXG4gICAgfVxyXG5cclxuICAgIHNldEl0ZW0oa2V5LCB2YWx1ZSl7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiSW5NZW1vcnlXZWJTdG9yYWdlLnNldEl0ZW1cIiwga2V5KTtcclxuICAgICAgICB0aGlzLl9kYXRhW2tleV0gPSB2YWx1ZTtcclxuICAgIH1cclxuXHJcbiAgICByZW1vdmVJdGVtKGtleSl7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiSW5NZW1vcnlXZWJTdG9yYWdlLnJlbW92ZUl0ZW1cIiwga2V5KTtcclxuICAgICAgICBkZWxldGUgdGhpcy5fZGF0YVtrZXldO1xyXG4gICAgfVxyXG5cclxuICAgIGdldCBsZW5ndGgoKSB7XHJcbiAgICAgICAgcmV0dXJuIE9iamVjdC5nZXRPd25Qcm9wZXJ0eU5hbWVzKHRoaXMuX2RhdGEpLmxlbmd0aDtcclxuICAgIH1cclxuXHJcbiAgICBrZXkoaW5kZXgpIHtcclxuICAgICAgICByZXR1cm4gT2JqZWN0LmdldE93blByb3BlcnR5TmFtZXModGhpcy5fZGF0YSlbaW5kZXhdO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IGp3cywgS2V5VXRpbCwgWDUwOSwgY3J5cHRvLCBoZXh0b2I2NHUsIGI2NHRvaGV4LCBBbGxvd2VkU2lnbmluZ0FsZ3MgfSBmcm9tICcuL2NyeXB0by9qc3JzYXNpZ24nO1xyXG5pbXBvcnQgZ2V0Sm9zZVV0aWwgZnJvbSAnLi9Kb3NlVXRpbEltcGwnO1xyXG5cclxuZXhwb3J0IGNvbnN0IEpvc2VVdGlsID0gZ2V0Sm9zZVV0aWwoeyBqd3MsIEtleVV0aWwsIFg1MDksIGNyeXB0bywgaGV4dG9iNjR1LCBiNjR0b2hleCwgQWxsb3dlZFNpZ25pbmdBbGdzIH0pO1xyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcblxyXG5leHBvcnQgZGVmYXVsdCBmdW5jdGlvbiBnZXRKb3NlVXRpbCh7IGp3cywgS2V5VXRpbCwgWDUwOSwgY3J5cHRvLCBoZXh0b2I2NHUsIGI2NHRvaGV4LCBBbGxvd2VkU2lnbmluZ0FsZ3MgfSkge1xyXG4gICAgcmV0dXJuIGNsYXNzIEpvc2VVdGlsIHtcclxuXHJcbiAgICAgICAgc3RhdGljIHBhcnNlSnd0KGp3dCkge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJKb3NlVXRpbC5wYXJzZUp3dFwiKTtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIHZhciB0b2tlbiA9IGp3cy5KV1MucGFyc2Uoand0KTtcclxuICAgICAgICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaGVhZGVyOiB0b2tlbi5oZWFkZXJPYmosXHJcbiAgICAgICAgICAgICAgICAgICAgcGF5bG9hZDogdG9rZW4ucGF5bG9hZE9ialxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZXJyb3IoZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHN0YXRpYyB2YWxpZGF0ZUp3dChqd3QsIGtleSwgaXNzdWVyLCBhdWRpZW5jZSwgY2xvY2tTa2V3LCBub3csIHRpbWVJbnNlbnNpdGl2ZSkge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJKb3NlVXRpbC52YWxpZGF0ZUp3dFwiKTtcclxuXHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICBpZiAoa2V5Lmt0eSA9PT0gXCJSU0FcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChrZXkuZSAmJiBrZXkubikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBrZXkgPSBLZXlVdGlsLmdldEtleShrZXkpO1xyXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSBpZiAoa2V5Lng1YyAmJiBrZXkueDVjLmxlbmd0aCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB2YXIgaGV4ID0gYjY0dG9oZXgoa2V5Lng1Y1swXSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGtleSA9IFg1MDkuZ2V0UHVibGljS2V5RnJvbUNlcnRIZXgoaGV4KTtcclxuICAgICAgICAgICAgICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJKb3NlVXRpbC52YWxpZGF0ZUp3dDogUlNBIGtleSBtaXNzaW5nIGtleSBtYXRlcmlhbFwiLCBrZXkpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiUlNBIGtleSBtaXNzaW5nIGtleSBtYXRlcmlhbFwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSBlbHNlIGlmIChrZXkua3R5ID09PSBcIkVDXCIpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAoa2V5LmNydiAmJiBrZXkueCAmJiBrZXkueSkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBrZXkgPSBLZXlVdGlsLmdldEtleShrZXkpO1xyXG4gICAgICAgICAgICAgICAgICAgIH0gZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIkpvc2VVdGlsLnZhbGlkYXRlSnd0OiBFQyBrZXkgbWlzc2luZyBrZXkgbWF0ZXJpYWxcIiwga2V5KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIkVDIGtleSBtaXNzaW5nIGtleSBtYXRlcmlhbFwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJKb3NlVXRpbC52YWxpZGF0ZUp3dDogVW5zdXBwb3J0ZWQga2V5IHR5cGVcIiwga2V5ICYmIGtleS5rdHkpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJVbnN1cHBvcnRlZCBrZXkgdHlwZTogXCIgKyBrZXkgJiYga2V5Lmt0eSkpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIHJldHVybiBKb3NlVXRpbC5fdmFsaWRhdGVKd3Qoand0LCBrZXksIGlzc3VlciwgYXVkaWVuY2UsIGNsb2NrU2tldywgbm93LCB0aW1lSW5zZW5zaXRpdmUpO1xyXG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZXJyb3IoZSAmJiBlLm1lc3NhZ2UgfHwgZSk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoXCJKV1QgdmFsaWRhdGlvbiBmYWlsZWRcIik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHN0YXRpYyB2YWxpZGF0ZUp3dEF0dHJpYnV0ZXMoand0LCBpc3N1ZXIsIGF1ZGllbmNlLCBjbG9ja1NrZXcsIG5vdywgdGltZUluc2Vuc2l0aXZlKSB7XHJcbiAgICAgICAgICAgIGlmICghY2xvY2tTa2V3KSB7XHJcbiAgICAgICAgICAgICAgICBjbG9ja1NrZXcgPSAwO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZiAoIW5vdykge1xyXG4gICAgICAgICAgICAgICAgbm93ID0gcGFyc2VJbnQoRGF0ZS5ub3coKSAvIDEwMDApO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICB2YXIgcGF5bG9hZCA9IEpvc2VVdGlsLnBhcnNlSnd0KGp3dCkucGF5bG9hZDtcclxuXHJcbiAgICAgICAgICAgIGlmICghcGF5bG9hZC5pc3MpIHtcclxuICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIkpvc2VVdGlsLl92YWxpZGF0ZUp3dDogaXNzdWVyIHdhcyBub3QgcHJvdmlkZWRcIik7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiaXNzdWVyIHdhcyBub3QgcHJvdmlkZWRcIikpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGlmIChwYXlsb2FkLmlzcyAhPT0gaXNzdWVyKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJKb3NlVXRpbC5fdmFsaWRhdGVKd3Q6IEludmFsaWQgaXNzdWVyIGluIHRva2VuXCIsIHBheWxvYWQuaXNzKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJJbnZhbGlkIGlzc3VlciBpbiB0b2tlbjogXCIgKyBwYXlsb2FkLmlzcykpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZiAoIXBheWxvYWQuYXVkKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJKb3NlVXRpbC5fdmFsaWRhdGVKd3Q6IGF1ZCB3YXMgbm90IHByb3ZpZGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcImF1ZCB3YXMgbm90IHByb3ZpZGVkXCIpKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB2YXIgdmFsaWRBdWRpZW5jZSA9IHBheWxvYWQuYXVkID09PSBhdWRpZW5jZSB8fCAoQXJyYXkuaXNBcnJheShwYXlsb2FkLmF1ZCkgJiYgcGF5bG9hZC5hdWQuaW5kZXhPZihhdWRpZW5jZSkgPj0gMCk7XHJcbiAgICAgICAgICAgIGlmICghdmFsaWRBdWRpZW5jZSkge1xyXG4gICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiSm9zZVV0aWwuX3ZhbGlkYXRlSnd0OiBJbnZhbGlkIGF1ZGllbmNlIGluIHRva2VuXCIsIHBheWxvYWQuYXVkKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJJbnZhbGlkIGF1ZGllbmNlIGluIHRva2VuOiBcIiArIHBheWxvYWQuYXVkKSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgaWYgKHBheWxvYWQuYXpwICYmIHBheWxvYWQuYXpwICE9PSBhdWRpZW5jZSkge1xyXG4gICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiSm9zZVV0aWwuX3ZhbGlkYXRlSnd0OiBJbnZhbGlkIGF6cCBpbiB0b2tlblwiLCBwYXlsb2FkLmF6cCk7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiSW52YWxpZCBhenAgaW4gdG9rZW46IFwiICsgcGF5bG9hZC5henApKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgaWYgKCF0aW1lSW5zZW5zaXRpdmUpIHtcclxuICAgICAgICAgICAgICAgIHZhciBsb3dlck5vdyA9IG5vdyArIGNsb2NrU2tldztcclxuICAgICAgICAgICAgICAgIHZhciB1cHBlck5vdyA9IG5vdyAtIGNsb2NrU2tldztcclxuXHJcbiAgICAgICAgICAgICAgICBpZiAoIXBheWxvYWQuaWF0KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiSm9zZVV0aWwuX3ZhbGlkYXRlSnd0OiBpYXQgd2FzIG5vdCBwcm92aWRlZFwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiaWF0IHdhcyBub3QgcHJvdmlkZWRcIikpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgaWYgKGxvd2VyTm93IDwgcGF5bG9hZC5pYXQpIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJKb3NlVXRpbC5fdmFsaWRhdGVKd3Q6IGlhdCBpcyBpbiB0aGUgZnV0dXJlXCIsIHBheWxvYWQuaWF0KTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiaWF0IGlzIGluIHRoZSBmdXR1cmU6IFwiICsgcGF5bG9hZC5pYXQpKTtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZiAocGF5bG9hZC5uYmYgJiYgbG93ZXJOb3cgPCBwYXlsb2FkLm5iZikge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIkpvc2VVdGlsLl92YWxpZGF0ZUp3dDogbmJmIGlzIGluIHRoZSBmdXR1cmVcIiwgcGF5bG9hZC5uYmYpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJuYmYgaXMgaW4gdGhlIGZ1dHVyZTogXCIgKyBwYXlsb2FkLm5iZikpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGlmICghcGF5bG9hZC5leHApIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJKb3NlVXRpbC5fdmFsaWRhdGVKd3Q6IGV4cCB3YXMgbm90IHByb3ZpZGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJleHAgd2FzIG5vdCBwcm92aWRlZFwiKSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZiAocGF5bG9hZC5leHAgPCB1cHBlck5vdykge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIkpvc2VVdGlsLl92YWxpZGF0ZUp3dDogZXhwIGlzIGluIHRoZSBwYXN0XCIsIHBheWxvYWQuZXhwKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiZXhwIGlzIGluIHRoZSBwYXN0OlwiICsgcGF5bG9hZC5leHApKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShwYXlsb2FkKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHN0YXRpYyBfdmFsaWRhdGVKd3Qoand0LCBrZXksIGlzc3VlciwgYXVkaWVuY2UsIGNsb2NrU2tldywgbm93LCB0aW1lSW5zZW5zaXRpdmUpIHtcclxuXHJcbiAgICAgICAgICAgIHJldHVybiBKb3NlVXRpbC52YWxpZGF0ZUp3dEF0dHJpYnV0ZXMoand0LCBpc3N1ZXIsIGF1ZGllbmNlLCBjbG9ja1NrZXcsIG5vdywgdGltZUluc2Vuc2l0aXZlKS50aGVuKHBheWxvYWQgPT4ge1xyXG4gICAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAoIWp3cy5KV1MudmVyaWZ5KGp3dCwga2V5LCBBbGxvd2VkU2lnbmluZ0FsZ3MpKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIkpvc2VVdGlsLl92YWxpZGF0ZUp3dDogc2lnbmF0dXJlIHZhbGlkYXRpb24gZmFpbGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwic2lnbmF0dXJlIHZhbGlkYXRpb24gZmFpbGVkXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiBwYXlsb2FkO1xyXG4gICAgICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihlICYmIGUubWVzc2FnZSB8fCBlKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwic2lnbmF0dXJlIHZhbGlkYXRpb24gZmFpbGVkXCIpKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBzdGF0aWMgaGFzaFN0cmluZyh2YWx1ZSwgYWxnKSB7XHJcbiAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gY3J5cHRvLlV0aWwuaGFzaFN0cmluZyh2YWx1ZSwgYWxnKTtcclxuICAgICAgICAgICAgfSBjYXRjaCAoZSkge1xyXG4gICAgICAgICAgICAgICAgTG9nLmVycm9yKGUpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBzdGF0aWMgaGV4VG9CYXNlNjRVcmwodmFsdWUpIHtcclxuICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgIHJldHVybiBoZXh0b2I2NHUodmFsdWUpO1xyXG4gICAgICAgICAgICB9IGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZXJyb3IoZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5pbXBvcnQgeyBHbG9iYWwgfSBmcm9tICcuL0dsb2JhbC5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgSnNvblNlcnZpY2Uge1xyXG4gICAgY29uc3RydWN0b3IoXHJcbiAgICAgICAgYWRkaXRpb25hbENvbnRlbnRUeXBlcyA9IG51bGwsIFxyXG4gICAgICAgIFhNTEh0dHBSZXF1ZXN0Q3RvciA9IEdsb2JhbC5YTUxIdHRwUmVxdWVzdCwgXHJcbiAgICAgICAgand0SGFuZGxlciA9IG51bGxcclxuICAgICkge1xyXG4gICAgICAgIGlmIChhZGRpdGlvbmFsQ29udGVudFR5cGVzICYmIEFycmF5LmlzQXJyYXkoYWRkaXRpb25hbENvbnRlbnRUeXBlcykpXHJcbiAgICAgICAge1xyXG4gICAgICAgICAgICB0aGlzLl9jb250ZW50VHlwZXMgPSBhZGRpdGlvbmFsQ29udGVudFR5cGVzLnNsaWNlKCk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2VcclxuICAgICAgICB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NvbnRlbnRUeXBlcyA9IFtdO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0aGlzLl9jb250ZW50VHlwZXMucHVzaCgnYXBwbGljYXRpb24vanNvbicpO1xyXG4gICAgICAgIGlmIChqd3RIYW5kbGVyKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NvbnRlbnRUeXBlcy5wdXNoKCdhcHBsaWNhdGlvbi9qd3QnKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMuX1hNTEh0dHBSZXF1ZXN0ID0gWE1MSHR0cFJlcXVlc3RDdG9yO1xyXG4gICAgICAgIHRoaXMuX2p3dEhhbmRsZXIgPSBqd3RIYW5kbGVyO1xyXG4gICAgfVxyXG5cclxuICAgIGdldEpzb24odXJsLCB0b2tlbikge1xyXG4gICAgICAgIGlmICghdXJsKXtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiSnNvblNlcnZpY2UuZ2V0SnNvbjogTm8gdXJsIHBhc3NlZFwiKTtcclxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwidXJsXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgTG9nLmRlYnVnKFwiSnNvblNlcnZpY2UuZ2V0SnNvbiwgdXJsOiBcIiwgdXJsKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuXHJcbiAgICAgICAgICAgIHZhciByZXEgPSBuZXcgdGhpcy5fWE1MSHR0cFJlcXVlc3QoKTtcclxuICAgICAgICAgICAgcmVxLm9wZW4oJ0dFVCcsIHVybCk7XHJcblxyXG4gICAgICAgICAgICB2YXIgYWxsb3dlZENvbnRlbnRUeXBlcyA9IHRoaXMuX2NvbnRlbnRUeXBlcztcclxuICAgICAgICAgICAgdmFyIGp3dEhhbmRsZXIgPSB0aGlzLl9qd3RIYW5kbGVyO1xyXG5cclxuICAgICAgICAgICAgcmVxLm9ubG9hZCA9IGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiSnNvblNlcnZpY2UuZ2V0SnNvbjogSFRUUCByZXNwb25zZSByZWNlaXZlZCwgc3RhdHVzXCIsIHJlcS5zdGF0dXMpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmIChyZXEuc3RhdHVzID09PSAyMDApIHtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgdmFyIGNvbnRlbnRUeXBlID0gcmVxLmdldFJlc3BvbnNlSGVhZGVyKFwiQ29udGVudC1UeXBlXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChjb250ZW50VHlwZSkge1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGZvdW5kID0gYWxsb3dlZENvbnRlbnRUeXBlcy5maW5kKGl0ZW09PntcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjb250ZW50VHlwZS5zdGFydHNXaXRoKGl0ZW0pKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGZvdW5kID09IFwiYXBwbGljYXRpb24vand0XCIpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGp3dEhhbmRsZXIocmVxKS50aGVuKHJlc29sdmUsIHJlamVjdCk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChmb3VuZCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXNvbHZlKEpTT04ucGFyc2UocmVxLnJlc3BvbnNlVGV4dCkpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiSnNvblNlcnZpY2UuZ2V0SnNvbjogRXJyb3IgcGFyc2luZyBKU09OIHJlc3BvbnNlXCIsIGUubWVzc2FnZSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KGUpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KEVycm9yKFwiSW52YWxpZCByZXNwb25zZSBDb250ZW50LVR5cGU6IFwiICsgY29udGVudFR5cGUgKyBcIiwgZnJvbSBVUkw6IFwiICsgdXJsKSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICByZWplY3QoRXJyb3IocmVxLnN0YXR1c1RleHQgKyBcIiAoXCIgKyByZXEuc3RhdHVzICsgXCIpXCIpKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgICAgIHJlcS5vbmVycm9yID0gZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJKc29uU2VydmljZS5nZXRKc29uOiBuZXR3b3JrIGVycm9yXCIpO1xyXG4gICAgICAgICAgICAgICAgcmVqZWN0KEVycm9yKFwiTmV0d29yayBFcnJvclwiKSk7XHJcbiAgICAgICAgICAgIH07XHJcblxyXG4gICAgICAgICAgICBpZiAodG9rZW4pIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIkpzb25TZXJ2aWNlLmdldEpzb246IHRva2VuIHBhc3NlZCwgc2V0dGluZyBBdXRob3JpemF0aW9uIGhlYWRlclwiKTtcclxuICAgICAgICAgICAgICAgIHJlcS5zZXRSZXF1ZXN0SGVhZGVyKFwiQXV0aG9yaXphdGlvblwiLCBcIkJlYXJlciBcIiArIHRva2VuKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmVxLnNlbmQoKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwb3N0Rm9ybSh1cmwsIHBheWxvYWQpIHtcclxuICAgICAgICBpZiAoIXVybCl7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIkpzb25TZXJ2aWNlLnBvc3RGb3JtOiBObyB1cmwgcGFzc2VkXCIpO1xyXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJ1cmxcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBMb2cuZGVidWcoXCJKc29uU2VydmljZS5wb3N0Rm9ybSwgdXJsOiBcIiwgdXJsKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIG5ldyBQcm9taXNlKChyZXNvbHZlLCByZWplY3QpID0+IHtcclxuXHJcbiAgICAgICAgICAgIHZhciByZXEgPSBuZXcgdGhpcy5fWE1MSHR0cFJlcXVlc3QoKTtcclxuICAgICAgICAgICAgcmVxLm9wZW4oJ1BPU1QnLCB1cmwpO1xyXG5cclxuICAgICAgICAgICAgdmFyIGFsbG93ZWRDb250ZW50VHlwZXMgPSB0aGlzLl9jb250ZW50VHlwZXM7XHJcblxyXG4gICAgICAgICAgICByZXEub25sb2FkID0gZnVuY3Rpb24oKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJKc29uU2VydmljZS5wb3N0Rm9ybTogSFRUUCByZXNwb25zZSByZWNlaXZlZCwgc3RhdHVzXCIsIHJlcS5zdGF0dXMpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmIChyZXEuc3RhdHVzID09PSAyMDApIHtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgdmFyIGNvbnRlbnRUeXBlID0gcmVxLmdldFJlc3BvbnNlSGVhZGVyKFwiQ29udGVudC1UeXBlXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChjb250ZW50VHlwZSkge1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGZvdW5kID0gYWxsb3dlZENvbnRlbnRUeXBlcy5maW5kKGl0ZW09PntcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjb250ZW50VHlwZS5zdGFydHNXaXRoKGl0ZW0pKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGZvdW5kKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlc29sdmUoSlNPTi5wYXJzZShyZXEucmVzcG9uc2VUZXh0KSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgY2F0Y2ggKGUpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJKc29uU2VydmljZS5wb3N0Rm9ybTogRXJyb3IgcGFyc2luZyBKU09OIHJlc3BvbnNlXCIsIGUubWVzc2FnZSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmVqZWN0KGUpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVqZWN0KEVycm9yKFwiSW52YWxpZCByZXNwb25zZSBDb250ZW50LVR5cGU6IFwiICsgY29udGVudFR5cGUgKyBcIiwgZnJvbSBVUkw6IFwiICsgdXJsKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGlmIChyZXEuc3RhdHVzID09PSA0MDApIHtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgdmFyIGNvbnRlbnRUeXBlID0gcmVxLmdldFJlc3BvbnNlSGVhZGVyKFwiQ29udGVudC1UeXBlXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChjb250ZW50VHlwZSkge1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgdmFyIGZvdW5kID0gYWxsb3dlZENvbnRlbnRUeXBlcy5maW5kKGl0ZW09PntcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChjb250ZW50VHlwZS5zdGFydHNXaXRoKGl0ZW0pKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRydWU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgaWYgKGZvdW5kKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0cnkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHZhciBwYXlsb2FkID0gSlNPTi5wYXJzZShyZXEucmVzcG9uc2VUZXh0KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAocGF5bG9hZCAmJiBwYXlsb2FkLmVycm9yKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIkpzb25TZXJ2aWNlLnBvc3RGb3JtOiBFcnJvciBmcm9tIHNlcnZlcjogXCIsIHBheWxvYWQuZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZWplY3QobmV3IEVycm9yKHBheWxvYWQuZXJyb3IpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGNhdGNoIChlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiSnNvblNlcnZpY2UucG9zdEZvcm06IEVycm9yIHBhcnNpbmcgSlNPTiByZXNwb25zZVwiLCBlLm1lc3NhZ2UpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlamVjdChlKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgcmVqZWN0KEVycm9yKHJlcS5zdGF0dXNUZXh0ICsgXCIgKFwiICsgcmVxLnN0YXR1cyArIFwiKVwiKSk7XHJcbiAgICAgICAgICAgIH07XHJcblxyXG4gICAgICAgICAgICByZXEub25lcnJvciA9IGZ1bmN0aW9uKCkge1xyXG4gICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiSnNvblNlcnZpY2UucG9zdEZvcm06IG5ldHdvcmsgZXJyb3JcIik7XHJcbiAgICAgICAgICAgICAgICByZWplY3QoRXJyb3IoXCJOZXR3b3JrIEVycm9yXCIpKTtcclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgICAgIGxldCBib2R5ID0gXCJcIjtcclxuICAgICAgICAgICAgZm9yKGxldCBrZXkgaW4gcGF5bG9hZCkge1xyXG5cclxuICAgICAgICAgICAgICAgIGxldCB2YWx1ZSA9IHBheWxvYWRba2V5XTtcclxuXHJcbiAgICAgICAgICAgICAgICBpZiAodmFsdWUpIHtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKGJvZHkubGVuZ3RoID4gMCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBib2R5ICs9IFwiJlwiO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgYm9keSArPSBlbmNvZGVVUklDb21wb25lbnQoa2V5KTtcclxuICAgICAgICAgICAgICAgICAgICBib2R5ICs9IFwiPVwiO1xyXG4gICAgICAgICAgICAgICAgICAgIGJvZHkgKz0gZW5jb2RlVVJJQ29tcG9uZW50KHZhbHVlKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmVxLnNldFJlcXVlc3RIZWFkZXIoXCJDb250ZW50LVR5cGVcIiwgXCJhcHBsaWNhdGlvbi94LXd3dy1mb3JtLXVybGVuY29kZWRcIik7XHJcbiAgICAgICAgICAgIHJlcS5zZW5kKGJvZHkpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmxldCBub3BMb2dnZXIgPSB7XHJcbiAgICBkZWJ1Zygpe30sXHJcbiAgICBpbmZvKCl7fSxcclxuICAgIHdhcm4oKXt9LFxyXG4gICAgZXJyb3IoKXt9XHJcbn07XHJcblxyXG5jb25zdCBOT05FID0gMDtcclxuY29uc3QgRVJST1IgPSAxO1xyXG5jb25zdCBXQVJOID0gMjtcclxuY29uc3QgSU5GTyA9IDM7XHJcbmNvbnN0IERFQlVHID0gNDtcclxuXHJcbmxldCBsb2dnZXI7XHJcbmxldCBsZXZlbDtcclxuXHJcbmV4cG9ydCBjbGFzcyBMb2cge1xyXG4gICAgc3RhdGljIGdldCBOT05FKCkge3JldHVybiBOT05FfTtcclxuICAgIHN0YXRpYyBnZXQgRVJST1IoKSB7cmV0dXJuIEVSUk9SfTtcclxuICAgIHN0YXRpYyBnZXQgV0FSTigpIHtyZXR1cm4gV0FSTn07XHJcbiAgICBzdGF0aWMgZ2V0IElORk8oKSB7cmV0dXJuIElORk99O1xyXG4gICAgc3RhdGljIGdldCBERUJVRygpIHtyZXR1cm4gREVCVUd9O1xyXG4gICAgXHJcbiAgICBzdGF0aWMgcmVzZXQoKXtcclxuICAgICAgICBsZXZlbCA9IElORk87XHJcbiAgICAgICAgbG9nZ2VyID0gbm9wTG9nZ2VyO1xyXG4gICAgfVxyXG4gICAgXHJcbiAgICBzdGF0aWMgZ2V0IGxldmVsKCl7XHJcbiAgICAgICAgcmV0dXJuIGxldmVsO1xyXG4gICAgfVxyXG4gICAgc3RhdGljIHNldCBsZXZlbCh2YWx1ZSl7XHJcbiAgICAgICAgaWYgKE5PTkUgPD0gdmFsdWUgJiYgdmFsdWUgPD0gREVCVUcpe1xyXG4gICAgICAgICAgICBsZXZlbCA9IHZhbHVlO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiSW52YWxpZCBsb2cgbGV2ZWxcIik7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgXHJcbiAgICBzdGF0aWMgZ2V0IGxvZ2dlcigpe1xyXG4gICAgICAgIHJldHVybiBsb2dnZXI7XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgc2V0IGxvZ2dlcih2YWx1ZSl7XHJcbiAgICAgICAgaWYgKCF2YWx1ZS5kZWJ1ZyAmJiB2YWx1ZS5pbmZvKSB7XHJcbiAgICAgICAgICAgIC8vIGp1c3QgdG8gc3RheSBiYWNrd2FyZHMgY29tcGF0LiBjYW4gcmVtb3ZlIGluIDIuMFxyXG4gICAgICAgICAgICB2YWx1ZS5kZWJ1ZyA9IHZhbHVlLmluZm87XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAodmFsdWUuZGVidWcgJiYgdmFsdWUuaW5mbyAmJiB2YWx1ZS53YXJuICYmIHZhbHVlLmVycm9yKXtcclxuICAgICAgICAgICAgbG9nZ2VyID0gdmFsdWU7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJJbnZhbGlkIGxvZ2dlclwiKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBcclxuICAgIHN0YXRpYyBkZWJ1ZyguLi5hcmdzKXtcclxuICAgICAgICBpZiAobGV2ZWwgPj0gREVCVUcpe1xyXG4gICAgICAgICAgICBsb2dnZXIuZGVidWcuYXBwbHkobG9nZ2VyLCBBcnJheS5mcm9tKGFyZ3MpKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbiAgICBzdGF0aWMgaW5mbyguLi5hcmdzKXtcclxuICAgICAgICBpZiAobGV2ZWwgPj0gSU5GTyl7XHJcbiAgICAgICAgICAgIGxvZ2dlci5pbmZvLmFwcGx5KGxvZ2dlciwgQXJyYXkuZnJvbShhcmdzKSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgc3RhdGljIHdhcm4oLi4uYXJncyl7XHJcbiAgICAgICAgaWYgKGxldmVsID49IFdBUk4pe1xyXG4gICAgICAgICAgICBsb2dnZXIud2Fybi5hcHBseShsb2dnZXIsIEFycmF5LmZyb20oYXJncykpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuICAgIHN0YXRpYyBlcnJvciguLi5hcmdzKXtcclxuICAgICAgICBpZiAobGV2ZWwgPj0gRVJST1Ipe1xyXG4gICAgICAgICAgICBsb2dnZXIuZXJyb3IuYXBwbHkobG9nZ2VyLCBBcnJheS5mcm9tKGFyZ3MpKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbn1cclxuXHJcbkxvZy5yZXNldCgpO1xyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcbmltcG9ydCB7IEpzb25TZXJ2aWNlIH0gZnJvbSAnLi9Kc29uU2VydmljZS5qcyc7XHJcblxyXG5jb25zdCBPaWRjTWV0YWRhdGFVcmxQYXRoID0gJy53ZWxsLWtub3duL29wZW5pZC1jb25maWd1cmF0aW9uJztcclxuXHJcbmV4cG9ydCBjbGFzcyBNZXRhZGF0YVNlcnZpY2Uge1xyXG4gICAgY29uc3RydWN0b3Ioc2V0dGluZ3MsIEpzb25TZXJ2aWNlQ3RvciA9IEpzb25TZXJ2aWNlKSB7XHJcbiAgICAgICAgaWYgKCFzZXR0aW5ncykge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJNZXRhZGF0YVNlcnZpY2U6IE5vIHNldHRpbmdzIHBhc3NlZCB0byBNZXRhZGF0YVNlcnZpY2VcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcInNldHRpbmdzXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fc2V0dGluZ3MgPSBzZXR0aW5ncztcclxuICAgICAgICB0aGlzLl9qc29uU2VydmljZSA9IG5ldyBKc29uU2VydmljZUN0b3IoWydhcHBsaWNhdGlvbi9qd2stc2V0K2pzb24nXSk7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IG1ldGFkYXRhVXJsKCkge1xyXG4gICAgICAgIGlmICghdGhpcy5fbWV0YWRhdGFVcmwpIHtcclxuICAgICAgICAgICAgaWYgKHRoaXMuX3NldHRpbmdzLm1ldGFkYXRhVXJsKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9tZXRhZGF0YVVybCA9IHRoaXMuX3NldHRpbmdzLm1ldGFkYXRhVXJsO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fbWV0YWRhdGFVcmwgPSB0aGlzLl9zZXR0aW5ncy5hdXRob3JpdHk7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYgKHRoaXMuX21ldGFkYXRhVXJsICYmIHRoaXMuX21ldGFkYXRhVXJsLmluZGV4T2YoT2lkY01ldGFkYXRhVXJsUGF0aCkgPCAwKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHRoaXMuX21ldGFkYXRhVXJsW3RoaXMuX21ldGFkYXRhVXJsLmxlbmd0aCAtIDFdICE9PSAnLycpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fbWV0YWRhdGFVcmwgKz0gJy8nO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9tZXRhZGF0YVVybCArPSBPaWRjTWV0YWRhdGFVcmxQYXRoO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fbWV0YWRhdGFVcmw7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0TWV0YWRhdGEoKSB7XHJcbiAgICAgICAgaWYgKHRoaXMuX3NldHRpbmdzLm1ldGFkYXRhKSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIk1ldGFkYXRhU2VydmljZS5nZXRNZXRhZGF0YTogUmV0dXJuaW5nIG1ldGFkYXRhIGZyb20gc2V0dGluZ3NcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUodGhpcy5fc2V0dGluZ3MubWV0YWRhdGEpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKCF0aGlzLm1ldGFkYXRhVXJsKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIk1ldGFkYXRhU2VydmljZS5nZXRNZXRhZGF0YTogTm8gYXV0aG9yaXR5IG9yIG1ldGFkYXRhVXJsIGNvbmZpZ3VyZWQgb24gc2V0dGluZ3NcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJObyBhdXRob3JpdHkgb3IgbWV0YWRhdGFVcmwgY29uZmlndXJlZCBvbiBzZXR0aW5nc1wiKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBMb2cuZGVidWcoXCJNZXRhZGF0YVNlcnZpY2UuZ2V0TWV0YWRhdGE6IGdldHRpbmcgbWV0YWRhdGEgZnJvbVwiLCB0aGlzLm1ldGFkYXRhVXJsKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2pzb25TZXJ2aWNlLmdldEpzb24odGhpcy5tZXRhZGF0YVVybClcclxuICAgICAgICAgICAgLnRoZW4obWV0YWRhdGEgPT4ge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiTWV0YWRhdGFTZXJ2aWNlLmdldE1ldGFkYXRhOiBqc29uIHJlY2VpdmVkXCIpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fc2V0dGluZ3MubWV0YWRhdGEgPSBtZXRhZGF0YTtcclxuICAgICAgICAgICAgICAgIHJldHVybiBtZXRhZGF0YTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0SXNzdWVyKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9nZXRNZXRhZGF0YVByb3BlcnR5KFwiaXNzdWVyXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIGdldEF1dGhvcml6YXRpb25FbmRwb2ludCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fZ2V0TWV0YWRhdGFQcm9wZXJ0eShcImF1dGhvcml6YXRpb25fZW5kcG9pbnRcIik7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0VXNlckluZm9FbmRwb2ludCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fZ2V0TWV0YWRhdGFQcm9wZXJ0eShcInVzZXJpbmZvX2VuZHBvaW50XCIpO1xyXG4gICAgfVxyXG5cclxuICAgIGdldFRva2VuRW5kcG9pbnQob3B0aW9uYWw9dHJ1ZSkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9nZXRNZXRhZGF0YVByb3BlcnR5KFwidG9rZW5fZW5kcG9pbnRcIiwgb3B0aW9uYWwpO1xyXG4gICAgfVxyXG5cclxuICAgIGdldENoZWNrU2Vzc2lvbklmcmFtZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fZ2V0TWV0YWRhdGFQcm9wZXJ0eShcImNoZWNrX3Nlc3Npb25faWZyYW1lXCIsIHRydWUpO1xyXG4gICAgfVxyXG5cclxuICAgIGdldEVuZFNlc3Npb25FbmRwb2ludCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fZ2V0TWV0YWRhdGFQcm9wZXJ0eShcImVuZF9zZXNzaW9uX2VuZHBvaW50XCIsIHRydWUpO1xyXG4gICAgfVxyXG5cclxuICAgIGdldFJldm9jYXRpb25FbmRwb2ludCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fZ2V0TWV0YWRhdGFQcm9wZXJ0eShcInJldm9jYXRpb25fZW5kcG9pbnRcIiwgdHJ1ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0S2V5c0VuZHBvaW50KCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9nZXRNZXRhZGF0YVByb3BlcnR5KFwiandrc191cmlcIiwgdHJ1ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgX2dldE1ldGFkYXRhUHJvcGVydHkobmFtZSwgb3B0aW9uYWw9ZmFsc2UpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJNZXRhZGF0YVNlcnZpY2UuZ2V0TWV0YWRhdGFQcm9wZXJ0eSBmb3I6IFwiICsgbmFtZSk7XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLmdldE1ldGFkYXRhKCkudGhlbihtZXRhZGF0YSA9PiB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIk1ldGFkYXRhU2VydmljZS5nZXRNZXRhZGF0YVByb3BlcnR5OiBtZXRhZGF0YSByZWNpZXZlZFwiKTtcclxuXHJcbiAgICAgICAgICAgIGlmIChtZXRhZGF0YVtuYW1lXSA9PT0gdW5kZWZpbmVkKSB7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYgKG9wdGlvbmFsID09PSB0cnVlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLndhcm4oXCJNZXRhZGF0YVNlcnZpY2UuZ2V0TWV0YWRhdGFQcm9wZXJ0eTogTWV0YWRhdGEgZG9lcyBub3QgY29udGFpbiBvcHRpb25hbCBwcm9wZXJ0eSBcIiArIG5hbWUpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB1bmRlZmluZWQ7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJNZXRhZGF0YVNlcnZpY2UuZ2V0TWV0YWRhdGFQcm9wZXJ0eTogTWV0YWRhdGEgZG9lcyBub3QgY29udGFpbiBwcm9wZXJ0eSBcIiArIG5hbWUpO1xyXG4gICAgICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIk1ldGFkYXRhIGRvZXMgbm90IGNvbnRhaW4gcHJvcGVydHkgXCIgKyBuYW1lKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIG1ldGFkYXRhW25hbWVdO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIGdldFNpZ25pbmdLZXlzKCkge1xyXG4gICAgICAgIGlmICh0aGlzLl9zZXR0aW5ncy5zaWduaW5nS2V5cykge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJNZXRhZGF0YVNlcnZpY2UuZ2V0U2lnbmluZ0tleXM6IFJldHVybmluZyBzaWduaW5nS2V5cyBmcm9tIHNldHRpbmdzXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHRoaXMuX3NldHRpbmdzLnNpZ25pbmdLZXlzKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLl9nZXRNZXRhZGF0YVByb3BlcnR5KFwiandrc191cmlcIikudGhlbihqd2tzX3VyaSA9PiB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIk1ldGFkYXRhU2VydmljZS5nZXRTaWduaW5nS2V5czogandrc191cmkgcmVjZWl2ZWRcIiwgandrc191cmkpO1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2pzb25TZXJ2aWNlLmdldEpzb24oandrc191cmkpLnRoZW4oa2V5U2V0ID0+IHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIk1ldGFkYXRhU2VydmljZS5nZXRTaWduaW5nS2V5czoga2V5IHNldCByZWNlaXZlZFwiLCBrZXlTZXQpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmICgha2V5U2V0LmtleXMpIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJNZXRhZGF0YVNlcnZpY2UuZ2V0U2lnbmluZ0tleXM6IE1pc3Npbmcga2V5cyBvbiBrZXlzZXRcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiTWlzc2luZyBrZXlzIG9uIGtleXNldFwiKTtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICB0aGlzLl9zZXR0aW5ncy5zaWduaW5nS2V5cyA9IGtleVNldC5rZXlzO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3NldHRpbmdzLnNpZ25pbmdLZXlzO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcbmltcG9ydCB7IE9pZGNDbGllbnRTZXR0aW5ncyB9IGZyb20gJy4vT2lkY0NsaWVudFNldHRpbmdzLmpzJztcclxuaW1wb3J0IHsgRXJyb3JSZXNwb25zZSB9IGZyb20gJy4vRXJyb3JSZXNwb25zZS5qcyc7XHJcbmltcG9ydCB7IFNpZ25pblJlcXVlc3QgfSBmcm9tICcuL1NpZ25pblJlcXVlc3QuanMnO1xyXG5pbXBvcnQgeyBTaWduaW5SZXNwb25zZSB9IGZyb20gJy4vU2lnbmluUmVzcG9uc2UuanMnO1xyXG5pbXBvcnQgeyBTaWdub3V0UmVxdWVzdCB9IGZyb20gJy4vU2lnbm91dFJlcXVlc3QuanMnO1xyXG5pbXBvcnQgeyBTaWdub3V0UmVzcG9uc2UgfSBmcm9tICcuL1NpZ25vdXRSZXNwb25zZS5qcyc7XHJcbmltcG9ydCB7IFNpZ25pblN0YXRlIH0gZnJvbSAnLi9TaWduaW5TdGF0ZS5qcyc7XHJcbmltcG9ydCB7IFN0YXRlIH0gZnJvbSAnLi9TdGF0ZS5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgT2lkY0NsaWVudCB7XHJcbiAgICBjb25zdHJ1Y3RvcihzZXR0aW5ncyA9IHt9KSB7XHJcbiAgICAgICAgaWYgKHNldHRpbmdzIGluc3RhbmNlb2YgT2lkY0NsaWVudFNldHRpbmdzKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX3NldHRpbmdzID0gc2V0dGluZ3M7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICB0aGlzLl9zZXR0aW5ncyA9IG5ldyBPaWRjQ2xpZW50U2V0dGluZ3Moc2V0dGluZ3MpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBnZXQgX3N0YXRlU3RvcmUoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuc2V0dGluZ3Muc3RhdGVTdG9yZTtcclxuICAgIH1cclxuICAgIGdldCBfdmFsaWRhdG9yKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLnZhbGlkYXRvcjtcclxuICAgIH1cclxuICAgIGdldCBfbWV0YWRhdGFTZXJ2aWNlKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLm1ldGFkYXRhU2VydmljZTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgc2V0dGluZ3MoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3NldHRpbmdzO1xyXG4gICAgfVxyXG4gICAgZ2V0IG1ldGFkYXRhU2VydmljZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fbWV0YWRhdGFTZXJ2aWNlO1xyXG4gICAgfVxyXG5cclxuICAgIGNyZWF0ZVNpZ25pblJlcXVlc3Qoe1xyXG4gICAgICAgIHJlc3BvbnNlX3R5cGUsIHNjb3BlLCByZWRpcmVjdF91cmksXHJcbiAgICAgICAgLy8gZGF0YSB3YXMgbWVhbnQgdG8gYmUgdGhlIHBsYWNlIGEgY2FsbGVyIGNvdWxkIGluZGljYXRlIHRoZSBkYXRhIHRvXHJcbiAgICAgICAgLy8gaGF2ZSByb3VuZCB0cmlwcGVkLCBidXQgcGVvcGxlIHdlcmUgZ2V0dGluZyBjb25mdXNlZCwgc28gaSBhZGRlZCBzdGF0ZSAoc2luY2UgdGhhdCBtYXRjaGVzIHRoZSBzcGVjKVxyXG4gICAgICAgIC8vIGFuZCBzbyBub3cgaWYgZGF0YSBpcyBub3QgcGFzc2VkLCBidXQgc3RhdGUgaXMgdGhlbiBzdGF0ZSB3aWxsIGJlIHVzZWRcclxuICAgICAgICBkYXRhLCBzdGF0ZSwgcHJvbXB0LCBkaXNwbGF5LCBtYXhfYWdlLCB1aV9sb2NhbGVzLCBpZF90b2tlbl9oaW50LCBsb2dpbl9oaW50LCBhY3JfdmFsdWVzLFxyXG4gICAgICAgIHJlc291cmNlLCByZXF1ZXN0LCByZXF1ZXN0X3VyaSwgcmVzcG9uc2VfbW9kZSwgZXh0cmFRdWVyeVBhcmFtcywgZXh0cmFUb2tlblBhcmFtcywgcmVxdWVzdF90eXBlLCBza2lwVXNlckluZm8gfSA9IHt9LFxyXG4gICAgICAgIHN0YXRlU3RvcmVcclxuICAgICkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIk9pZGNDbGllbnQuY3JlYXRlU2lnbmluUmVxdWVzdFwiKTtcclxuXHJcbiAgICAgICAgbGV0IGNsaWVudF9pZCA9IHRoaXMuX3NldHRpbmdzLmNsaWVudF9pZDtcclxuICAgICAgICByZXNwb25zZV90eXBlID0gcmVzcG9uc2VfdHlwZSB8fCB0aGlzLl9zZXR0aW5ncy5yZXNwb25zZV90eXBlO1xyXG4gICAgICAgIHNjb3BlID0gc2NvcGUgfHwgdGhpcy5fc2V0dGluZ3Muc2NvcGU7XHJcbiAgICAgICAgcmVkaXJlY3RfdXJpID0gcmVkaXJlY3RfdXJpIHx8IHRoaXMuX3NldHRpbmdzLnJlZGlyZWN0X3VyaTtcclxuXHJcbiAgICAgICAgLy8gaWRfdG9rZW5faGludCwgbG9naW5faGludCBhcmVuJ3QgYWxsb3dlZCBvbiBfc2V0dGluZ3NcclxuICAgICAgICBwcm9tcHQgPSBwcm9tcHQgfHwgdGhpcy5fc2V0dGluZ3MucHJvbXB0O1xyXG4gICAgICAgIGRpc3BsYXkgPSBkaXNwbGF5IHx8IHRoaXMuX3NldHRpbmdzLmRpc3BsYXk7XHJcbiAgICAgICAgbWF4X2FnZSA9IG1heF9hZ2UgfHwgdGhpcy5fc2V0dGluZ3MubWF4X2FnZTtcclxuICAgICAgICB1aV9sb2NhbGVzID0gdWlfbG9jYWxlcyB8fCB0aGlzLl9zZXR0aW5ncy51aV9sb2NhbGVzO1xyXG4gICAgICAgIGFjcl92YWx1ZXMgPSBhY3JfdmFsdWVzIHx8IHRoaXMuX3NldHRpbmdzLmFjcl92YWx1ZXM7XHJcbiAgICAgICAgcmVzb3VyY2UgPSByZXNvdXJjZSB8fCB0aGlzLl9zZXR0aW5ncy5yZXNvdXJjZTtcclxuICAgICAgICByZXNwb25zZV9tb2RlID0gcmVzcG9uc2VfbW9kZSB8fCB0aGlzLl9zZXR0aW5ncy5yZXNwb25zZV9tb2RlO1xyXG4gICAgICAgIGV4dHJhUXVlcnlQYXJhbXMgPSBleHRyYVF1ZXJ5UGFyYW1zIHx8IHRoaXMuX3NldHRpbmdzLmV4dHJhUXVlcnlQYXJhbXM7XHJcbiAgICAgICAgZXh0cmFUb2tlblBhcmFtcyA9IGV4dHJhVG9rZW5QYXJhbXMgfHwgdGhpcy5fc2V0dGluZ3MuZXh0cmFUb2tlblBhcmFtcztcclxuXHJcbiAgICAgICAgbGV0IGF1dGhvcml0eSA9IHRoaXMuX3NldHRpbmdzLmF1dGhvcml0eTtcclxuXHJcbiAgICAgICAgaWYgKFNpZ25pblJlcXVlc3QuaXNDb2RlKHJlc3BvbnNlX3R5cGUpICYmIHJlc3BvbnNlX3R5cGUgIT09IFwiY29kZVwiKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJPcGVuSUQgQ29ubmVjdCBoeWJyaWQgZmxvdyBpcyBub3Qgc3VwcG9ydGVkXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLl9tZXRhZGF0YVNlcnZpY2UuZ2V0QXV0aG9yaXphdGlvbkVuZHBvaW50KCkudGhlbih1cmwgPT4ge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJPaWRjQ2xpZW50LmNyZWF0ZVNpZ25pblJlcXVlc3Q6IFJlY2VpdmVkIGF1dGhvcml6YXRpb24gZW5kcG9pbnRcIiwgdXJsKTtcclxuXHJcbiAgICAgICAgICAgIGxldCBzaWduaW5SZXF1ZXN0ID0gbmV3IFNpZ25pblJlcXVlc3Qoe1xyXG4gICAgICAgICAgICAgICAgdXJsLFxyXG4gICAgICAgICAgICAgICAgY2xpZW50X2lkLFxyXG4gICAgICAgICAgICAgICAgcmVkaXJlY3RfdXJpLFxyXG4gICAgICAgICAgICAgICAgcmVzcG9uc2VfdHlwZSxcclxuICAgICAgICAgICAgICAgIHNjb3BlLFxyXG4gICAgICAgICAgICAgICAgZGF0YTogZGF0YSB8fCBzdGF0ZSxcclxuICAgICAgICAgICAgICAgIGF1dGhvcml0eSxcclxuICAgICAgICAgICAgICAgIHByb21wdCwgZGlzcGxheSwgbWF4X2FnZSwgdWlfbG9jYWxlcywgaWRfdG9rZW5faGludCwgbG9naW5faGludCwgYWNyX3ZhbHVlcyxcclxuICAgICAgICAgICAgICAgIHJlc291cmNlLCByZXF1ZXN0LCByZXF1ZXN0X3VyaSwgZXh0cmFRdWVyeVBhcmFtcywgZXh0cmFUb2tlblBhcmFtcywgcmVxdWVzdF90eXBlLCByZXNwb25zZV9tb2RlLFxyXG4gICAgICAgICAgICAgICAgY2xpZW50X3NlY3JldDogdGhpcy5fc2V0dGluZ3MuY2xpZW50X3NlY3JldCxcclxuICAgICAgICAgICAgICAgIHNraXBVc2VySW5mb1xyXG4gICAgICAgICAgICB9KTtcclxuXHJcbiAgICAgICAgICAgIHZhciBzaWduaW5TdGF0ZSA9IHNpZ25pblJlcXVlc3Quc3RhdGU7XHJcbiAgICAgICAgICAgIHN0YXRlU3RvcmUgPSBzdGF0ZVN0b3JlIHx8IHRoaXMuX3N0YXRlU3RvcmU7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gc3RhdGVTdG9yZS5zZXQoc2lnbmluU3RhdGUuaWQsIHNpZ25pblN0YXRlLnRvU3RvcmFnZVN0cmluZygpKS50aGVuKCgpID0+IHtcclxuICAgICAgICAgICAgICAgIHJldHVybiBzaWduaW5SZXF1ZXN0O1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICByZWFkU2lnbmluUmVzcG9uc2VTdGF0ZSh1cmwsIHN0YXRlU3RvcmUsIHJlbW92ZVN0YXRlID0gZmFsc2UpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJPaWRjQ2xpZW50LnJlYWRTaWduaW5SZXNwb25zZVN0YXRlXCIpO1xyXG5cclxuICAgICAgICBsZXQgdXNlUXVlcnkgPSB0aGlzLl9zZXR0aW5ncy5yZXNwb25zZV9tb2RlID09PSBcInF1ZXJ5XCIgfHwgXHJcbiAgICAgICAgICAgICghdGhpcy5fc2V0dGluZ3MucmVzcG9uc2VfbW9kZSAmJiBTaWduaW5SZXF1ZXN0LmlzQ29kZSh0aGlzLl9zZXR0aW5ncy5yZXNwb25zZV90eXBlKSk7XHJcbiAgICAgICAgbGV0IGRlbGltaXRlciA9IHVzZVF1ZXJ5ID8gXCI/XCIgOiBcIiNcIjtcclxuXHJcbiAgICAgICAgdmFyIHJlc3BvbnNlID0gbmV3IFNpZ25pblJlc3BvbnNlKHVybCwgZGVsaW1pdGVyKTtcclxuXHJcbiAgICAgICAgaWYgKCFyZXNwb25zZS5zdGF0ZSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJPaWRjQ2xpZW50LnJlYWRTaWduaW5SZXNwb25zZVN0YXRlOiBObyBzdGF0ZSBpbiByZXNwb25zZVwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5vIHN0YXRlIGluIHJlc3BvbnNlXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHN0YXRlU3RvcmUgPSBzdGF0ZVN0b3JlIHx8IHRoaXMuX3N0YXRlU3RvcmU7XHJcblxyXG4gICAgICAgIHZhciBzdGF0ZUFwaSA9IHJlbW92ZVN0YXRlID8gc3RhdGVTdG9yZS5yZW1vdmUuYmluZChzdGF0ZVN0b3JlKSA6IHN0YXRlU3RvcmUuZ2V0LmJpbmQoc3RhdGVTdG9yZSk7XHJcblxyXG4gICAgICAgIHJldHVybiBzdGF0ZUFwaShyZXNwb25zZS5zdGF0ZSkudGhlbihzdG9yZWRTdGF0ZVN0cmluZyA9PiB7XHJcbiAgICAgICAgICAgIGlmICghc3RvcmVkU3RhdGVTdHJpbmcpIHtcclxuICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIk9pZGNDbGllbnQucmVhZFNpZ25pblJlc3BvbnNlU3RhdGU6IE5vIG1hdGNoaW5nIHN0YXRlIGZvdW5kIGluIHN0b3JhZ2VcIik7XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJObyBtYXRjaGluZyBzdGF0ZSBmb3VuZCBpbiBzdG9yYWdlXCIpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBsZXQgc3RhdGUgPSBTaWduaW5TdGF0ZS5mcm9tU3RvcmFnZVN0cmluZyhzdG9yZWRTdGF0ZVN0cmluZyk7XHJcbiAgICAgICAgICAgIHJldHVybiB7c3RhdGUsIHJlc3BvbnNlfTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBwcm9jZXNzU2lnbmluUmVzcG9uc2UodXJsLCBzdGF0ZVN0b3JlKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiT2lkY0NsaWVudC5wcm9jZXNzU2lnbmluUmVzcG9uc2VcIik7XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLnJlYWRTaWduaW5SZXNwb25zZVN0YXRlKHVybCwgc3RhdGVTdG9yZSwgdHJ1ZSkudGhlbigoe3N0YXRlLCByZXNwb25zZX0pID0+IHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiT2lkY0NsaWVudC5wcm9jZXNzU2lnbmluUmVzcG9uc2U6IFJlY2VpdmVkIHN0YXRlIGZyb20gc3RvcmFnZTsgdmFsaWRhdGluZyByZXNwb25zZVwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3ZhbGlkYXRvci52YWxpZGF0ZVNpZ25pblJlc3BvbnNlKHN0YXRlLCByZXNwb25zZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgY3JlYXRlU2lnbm91dFJlcXVlc3Qoe2lkX3Rva2VuX2hpbnQsIGRhdGEsIHN0YXRlLCBwb3N0X2xvZ291dF9yZWRpcmVjdF91cmksIGV4dHJhUXVlcnlQYXJhbXMsIHJlcXVlc3RfdHlwZSB9ID0ge30sXHJcbiAgICAgICAgc3RhdGVTdG9yZVxyXG4gICAgKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiT2lkY0NsaWVudC5jcmVhdGVTaWdub3V0UmVxdWVzdFwiKTtcclxuXHJcbiAgICAgICAgcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpID0gcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpIHx8IHRoaXMuX3NldHRpbmdzLnBvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaTtcclxuICAgICAgICBleHRyYVF1ZXJ5UGFyYW1zID0gZXh0cmFRdWVyeVBhcmFtcyB8fCB0aGlzLl9zZXR0aW5ncy5leHRyYVF1ZXJ5UGFyYW1zO1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fbWV0YWRhdGFTZXJ2aWNlLmdldEVuZFNlc3Npb25FbmRwb2ludCgpLnRoZW4odXJsID0+IHtcclxuICAgICAgICAgICAgaWYgKCF1cmwpIHtcclxuICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIk9pZGNDbGllbnQuY3JlYXRlU2lnbm91dFJlcXVlc3Q6IE5vIGVuZCBzZXNzaW9uIGVuZHBvaW50IHVybCByZXR1cm5lZFwiKTtcclxuICAgICAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIm5vIGVuZCBzZXNzaW9uIGVuZHBvaW50XCIpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJPaWRjQ2xpZW50LmNyZWF0ZVNpZ25vdXRSZXF1ZXN0OiBSZWNlaXZlZCBlbmQgc2Vzc2lvbiBlbmRwb2ludFwiLCB1cmwpO1xyXG5cclxuICAgICAgICAgICAgbGV0IHJlcXVlc3QgPSBuZXcgU2lnbm91dFJlcXVlc3Qoe1xyXG4gICAgICAgICAgICAgICAgdXJsLFxyXG4gICAgICAgICAgICAgICAgaWRfdG9rZW5faGludCxcclxuICAgICAgICAgICAgICAgIHBvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaSxcclxuICAgICAgICAgICAgICAgIGRhdGE6IGRhdGEgfHwgc3RhdGUsXHJcbiAgICAgICAgICAgICAgICBleHRyYVF1ZXJ5UGFyYW1zLFxyXG4gICAgICAgICAgICAgICAgcmVxdWVzdF90eXBlXHJcbiAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgdmFyIHNpZ25vdXRTdGF0ZSA9IHJlcXVlc3Quc3RhdGU7XHJcbiAgICAgICAgICAgIGlmIChzaWdub3V0U3RhdGUpIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIk9pZGNDbGllbnQuY3JlYXRlU2lnbm91dFJlcXVlc3Q6IFNpZ25vdXQgcmVxdWVzdCBoYXMgc3RhdGUgdG8gcGVyc2lzdFwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICBzdGF0ZVN0b3JlID0gc3RhdGVTdG9yZSB8fCB0aGlzLl9zdGF0ZVN0b3JlO1xyXG4gICAgICAgICAgICAgICAgc3RhdGVTdG9yZS5zZXQoc2lnbm91dFN0YXRlLmlkLCBzaWdub3V0U3RhdGUudG9TdG9yYWdlU3RyaW5nKCkpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gcmVxdWVzdDtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICByZWFkU2lnbm91dFJlc3BvbnNlU3RhdGUodXJsLCBzdGF0ZVN0b3JlLCByZW1vdmVTdGF0ZSA9IGZhbHNlKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiT2lkY0NsaWVudC5yZWFkU2lnbm91dFJlc3BvbnNlU3RhdGVcIik7XHJcblxyXG4gICAgICAgIHZhciByZXNwb25zZSA9IG5ldyBTaWdub3V0UmVzcG9uc2UodXJsKTtcclxuICAgICAgICBpZiAoIXJlc3BvbnNlLnN0YXRlKSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIk9pZGNDbGllbnQucmVhZFNpZ25vdXRSZXNwb25zZVN0YXRlOiBObyBzdGF0ZSBpbiByZXNwb25zZVwiKTtcclxuXHJcbiAgICAgICAgICAgIGlmIChyZXNwb25zZS5lcnJvcikge1xyXG4gICAgICAgICAgICAgICAgTG9nLndhcm4oXCJPaWRjQ2xpZW50LnJlYWRTaWdub3V0UmVzcG9uc2VTdGF0ZTogUmVzcG9uc2Ugd2FzIGVycm9yOiBcIiwgcmVzcG9uc2UuZXJyb3IpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvclJlc3BvbnNlKHJlc3BvbnNlKSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoe3VuZGVmaW5lZCwgcmVzcG9uc2V9KTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHZhciBzdGF0ZUtleSA9IHJlc3BvbnNlLnN0YXRlO1xyXG5cclxuICAgICAgICBzdGF0ZVN0b3JlID0gc3RhdGVTdG9yZSB8fCB0aGlzLl9zdGF0ZVN0b3JlO1xyXG5cclxuICAgICAgICB2YXIgc3RhdGVBcGkgPSByZW1vdmVTdGF0ZSA/IHN0YXRlU3RvcmUucmVtb3ZlLmJpbmQoc3RhdGVTdG9yZSkgOiBzdGF0ZVN0b3JlLmdldC5iaW5kKHN0YXRlU3RvcmUpO1xyXG4gICAgICAgIHJldHVybiBzdGF0ZUFwaShzdGF0ZUtleSkudGhlbihzdG9yZWRTdGF0ZVN0cmluZyA9PiB7XHJcbiAgICAgICAgICAgIGlmICghc3RvcmVkU3RhdGVTdHJpbmcpIHtcclxuICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIk9pZGNDbGllbnQucmVhZFNpZ25vdXRSZXNwb25zZVN0YXRlOiBObyBtYXRjaGluZyBzdGF0ZSBmb3VuZCBpbiBzdG9yYWdlXCIpO1xyXG4gICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiTm8gbWF0Y2hpbmcgc3RhdGUgZm91bmQgaW4gc3RvcmFnZVwiKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgbGV0IHN0YXRlID0gU3RhdGUuZnJvbVN0b3JhZ2VTdHJpbmcoc3RvcmVkU3RhdGVTdHJpbmcpO1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHtzdGF0ZSwgcmVzcG9uc2V9O1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHByb2Nlc3NTaWdub3V0UmVzcG9uc2UodXJsLCBzdGF0ZVN0b3JlKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiT2lkY0NsaWVudC5wcm9jZXNzU2lnbm91dFJlc3BvbnNlXCIpO1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5yZWFkU2lnbm91dFJlc3BvbnNlU3RhdGUodXJsLCBzdGF0ZVN0b3JlLCB0cnVlKS50aGVuKCh7c3RhdGUsIHJlc3BvbnNlfSkgPT4ge1xyXG4gICAgICAgICAgICBpZiAoc3RhdGUpIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIk9pZGNDbGllbnQucHJvY2Vzc1NpZ25vdXRSZXNwb25zZTogUmVjZWl2ZWQgc3RhdGUgZnJvbSBzdG9yYWdlOyB2YWxpZGF0aW5nIHJlc3BvbnNlXCIpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3ZhbGlkYXRvci52YWxpZGF0ZVNpZ25vdXRSZXNwb25zZShzdGF0ZSwgcmVzcG9uc2UpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiT2lkY0NsaWVudC5wcm9jZXNzU2lnbm91dFJlc3BvbnNlOiBObyBzdGF0ZSBmcm9tIHN0b3JhZ2U7IHNraXBwaW5nIHZhbGlkYXRpbmcgcmVzcG9uc2VcIik7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gcmVzcG9uc2U7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBjbGVhclN0YWxlU3RhdGUoc3RhdGVTdG9yZSkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIk9pZGNDbGllbnQuY2xlYXJTdGFsZVN0YXRlXCIpO1xyXG5cclxuICAgICAgICBzdGF0ZVN0b3JlID0gc3RhdGVTdG9yZSB8fCB0aGlzLl9zdGF0ZVN0b3JlO1xyXG5cclxuICAgICAgICByZXR1cm4gU3RhdGUuY2xlYXJTdGFsZVN0YXRlKHN0YXRlU3RvcmUsIHRoaXMuc2V0dGluZ3Muc3RhbGVTdGF0ZUFnZSk7XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5pbXBvcnQgeyBXZWJTdG9yYWdlU3RhdGVTdG9yZSB9IGZyb20gJy4vV2ViU3RvcmFnZVN0YXRlU3RvcmUuanMnO1xyXG5pbXBvcnQgeyBSZXNwb25zZVZhbGlkYXRvciB9IGZyb20gJy4vUmVzcG9uc2VWYWxpZGF0b3IuanMnO1xyXG5pbXBvcnQgeyBNZXRhZGF0YVNlcnZpY2UgfSBmcm9tICcuL01ldGFkYXRhU2VydmljZS5qcyc7XHJcblxyXG5jb25zdCBPaWRjTWV0YWRhdGFVcmxQYXRoID0gJy53ZWxsLWtub3duL29wZW5pZC1jb25maWd1cmF0aW9uJztcclxuXHJcbmNvbnN0IERlZmF1bHRSZXNwb25zZVR5cGUgPSBcImlkX3Rva2VuXCI7XHJcbmNvbnN0IERlZmF1bHRTY29wZSA9IFwib3BlbmlkXCI7XHJcbmNvbnN0IERlZmF1bHRTdGFsZVN0YXRlQWdlID0gNjAgKiAxNTsgLy8gc2Vjb25kc1xyXG5jb25zdCBEZWZhdWx0Q2xvY2tTa2V3SW5TZWNvbmRzID0gNjAgKiA1O1xyXG5cclxuZXhwb3J0IGNsYXNzIE9pZGNDbGllbnRTZXR0aW5ncyB7XHJcbiAgICBjb25zdHJ1Y3Rvcih7XHJcbiAgICAgICAgLy8gbWV0YWRhdGEgcmVsYXRlZFxyXG4gICAgICAgIGF1dGhvcml0eSwgbWV0YWRhdGFVcmwsIG1ldGFkYXRhLCBzaWduaW5nS2V5cyxcclxuICAgICAgICAvLyBjbGllbnQgcmVsYXRlZFxyXG4gICAgICAgIGNsaWVudF9pZCwgY2xpZW50X3NlY3JldCwgcmVzcG9uc2VfdHlwZSA9IERlZmF1bHRSZXNwb25zZVR5cGUsIHNjb3BlID0gRGVmYXVsdFNjb3BlLFxyXG4gICAgICAgIHJlZGlyZWN0X3VyaSwgcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpLFxyXG4gICAgICAgIC8vIG9wdGlvbmFsIHByb3RvY29sXHJcbiAgICAgICAgcHJvbXB0LCBkaXNwbGF5LCBtYXhfYWdlLCB1aV9sb2NhbGVzLCBhY3JfdmFsdWVzLCByZXNvdXJjZSwgcmVzcG9uc2VfbW9kZSxcclxuICAgICAgICAvLyBiZWhhdmlvciBmbGFnc1xyXG4gICAgICAgIGZpbHRlclByb3RvY29sQ2xhaW1zID0gdHJ1ZSwgbG9hZFVzZXJJbmZvID0gdHJ1ZSxcclxuICAgICAgICBzdGFsZVN0YXRlQWdlID0gRGVmYXVsdFN0YWxlU3RhdGVBZ2UsIGNsb2NrU2tldyA9IERlZmF1bHRDbG9ja1NrZXdJblNlY29uZHMsXHJcbiAgICAgICAgdXNlckluZm9Kd3RJc3N1ZXIgPSAnT1AnLFxyXG4gICAgICAgIC8vIG90aGVyIGJlaGF2aW9yXHJcbiAgICAgICAgc3RhdGVTdG9yZSA9IG5ldyBXZWJTdG9yYWdlU3RhdGVTdG9yZSgpLFxyXG4gICAgICAgIFJlc3BvbnNlVmFsaWRhdG9yQ3RvciA9IFJlc3BvbnNlVmFsaWRhdG9yLFxyXG4gICAgICAgIE1ldGFkYXRhU2VydmljZUN0b3IgPSBNZXRhZGF0YVNlcnZpY2UsXHJcbiAgICAgICAgLy8gZXh0cmEgcXVlcnkgcGFyYW1zXHJcbiAgICAgICAgZXh0cmFRdWVyeVBhcmFtcyA9IHt9LFxyXG4gICAgICAgIGV4dHJhVG9rZW5QYXJhbXMgPSB7fVxyXG4gICAgfSA9IHt9KSB7XHJcblxyXG4gICAgICAgIHRoaXMuX2F1dGhvcml0eSA9IGF1dGhvcml0eTtcclxuICAgICAgICB0aGlzLl9tZXRhZGF0YVVybCA9IG1ldGFkYXRhVXJsO1xyXG4gICAgICAgIHRoaXMuX21ldGFkYXRhID0gbWV0YWRhdGE7XHJcbiAgICAgICAgdGhpcy5fc2lnbmluZ0tleXMgPSBzaWduaW5nS2V5cztcclxuXHJcbiAgICAgICAgdGhpcy5fY2xpZW50X2lkID0gY2xpZW50X2lkO1xyXG4gICAgICAgIHRoaXMuX2NsaWVudF9zZWNyZXQgPSBjbGllbnRfc2VjcmV0O1xyXG4gICAgICAgIHRoaXMuX3Jlc3BvbnNlX3R5cGUgPSByZXNwb25zZV90eXBlO1xyXG4gICAgICAgIHRoaXMuX3Njb3BlID0gc2NvcGU7XHJcbiAgICAgICAgdGhpcy5fcmVkaXJlY3RfdXJpID0gcmVkaXJlY3RfdXJpO1xyXG4gICAgICAgIHRoaXMuX3Bvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaSA9IHBvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaTtcclxuXHJcbiAgICAgICAgdGhpcy5fcHJvbXB0ID0gcHJvbXB0O1xyXG4gICAgICAgIHRoaXMuX2Rpc3BsYXkgPSBkaXNwbGF5O1xyXG4gICAgICAgIHRoaXMuX21heF9hZ2UgPSBtYXhfYWdlO1xyXG4gICAgICAgIHRoaXMuX3VpX2xvY2FsZXMgPSB1aV9sb2NhbGVzO1xyXG4gICAgICAgIHRoaXMuX2Fjcl92YWx1ZXMgPSBhY3JfdmFsdWVzO1xyXG4gICAgICAgIHRoaXMuX3Jlc291cmNlID0gcmVzb3VyY2U7XHJcbiAgICAgICAgdGhpcy5fcmVzcG9uc2VfbW9kZSA9IHJlc3BvbnNlX21vZGU7XHJcblxyXG4gICAgICAgIHRoaXMuX2ZpbHRlclByb3RvY29sQ2xhaW1zID0gISFmaWx0ZXJQcm90b2NvbENsYWltcztcclxuICAgICAgICB0aGlzLl9sb2FkVXNlckluZm8gPSAhIWxvYWRVc2VySW5mbztcclxuICAgICAgICB0aGlzLl9zdGFsZVN0YXRlQWdlID0gc3RhbGVTdGF0ZUFnZTtcclxuICAgICAgICB0aGlzLl9jbG9ja1NrZXcgPSBjbG9ja1NrZXc7XHJcbiAgICAgICAgdGhpcy5fdXNlckluZm9Kd3RJc3N1ZXIgPSB1c2VySW5mb0p3dElzc3VlcjtcclxuXHJcbiAgICAgICAgdGhpcy5fc3RhdGVTdG9yZSA9IHN0YXRlU3RvcmU7XHJcbiAgICAgICAgdGhpcy5fdmFsaWRhdG9yID0gbmV3IFJlc3BvbnNlVmFsaWRhdG9yQ3Rvcih0aGlzKTtcclxuICAgICAgICB0aGlzLl9tZXRhZGF0YVNlcnZpY2UgPSBuZXcgTWV0YWRhdGFTZXJ2aWNlQ3Rvcih0aGlzKTtcclxuXHJcbiAgICAgICAgdGhpcy5fZXh0cmFRdWVyeVBhcmFtcyA9IHR5cGVvZiBleHRyYVF1ZXJ5UGFyYW1zID09PSAnb2JqZWN0JyA/IGV4dHJhUXVlcnlQYXJhbXMgOiB7fTtcclxuICAgICAgICB0aGlzLl9leHRyYVRva2VuUGFyYW1zID0gdHlwZW9mIGV4dHJhVG9rZW5QYXJhbXMgPT09ICdvYmplY3QnID8gZXh0cmFUb2tlblBhcmFtcyA6IHt9O1xyXG4gICAgfVxyXG5cclxuICAgIC8vIGNsaWVudCBjb25maWdcclxuICAgIGdldCBjbGllbnRfaWQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NsaWVudF9pZDtcclxuICAgIH1cclxuICAgIHNldCBjbGllbnRfaWQodmFsdWUpIHtcclxuICAgICAgICBpZiAoIXRoaXMuX2NsaWVudF9pZCkge1xyXG4gICAgICAgICAgICAvLyBvbmUtdGltZSBzZXQgb25seVxyXG4gICAgICAgICAgICB0aGlzLl9jbGllbnRfaWQgPSB2YWx1ZTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIk9pZGNDbGllbnRTZXR0aW5ncy5zZXRfY2xpZW50X2lkOiBjbGllbnRfaWQgaGFzIGFscmVhZHkgYmVlbiBhc3NpZ25lZC5cIilcclxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiY2xpZW50X2lkIGhhcyBhbHJlYWR5IGJlZW4gYXNzaWduZWQuXCIpXHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgZ2V0IGNsaWVudF9zZWNyZXQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NsaWVudF9zZWNyZXQ7XHJcbiAgICB9XHJcbiAgICBnZXQgcmVzcG9uc2VfdHlwZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fcmVzcG9uc2VfdHlwZTtcclxuICAgIH1cclxuICAgIGdldCBzY29wZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc2NvcGU7XHJcbiAgICB9XHJcbiAgICBnZXQgcmVkaXJlY3RfdXJpKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9yZWRpcmVjdF91cmk7XHJcbiAgICB9XHJcbiAgICBnZXQgcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9wb3N0X2xvZ291dF9yZWRpcmVjdF91cmk7XHJcbiAgICB9XHJcblxyXG5cclxuICAgIC8vIG9wdGlvbmFsIHByb3RvY29sIHBhcmFtc1xyXG4gICAgZ2V0IHByb21wdCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fcHJvbXB0O1xyXG4gICAgfVxyXG4gICAgZ2V0IGRpc3BsYXkoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2Rpc3BsYXk7XHJcbiAgICB9XHJcbiAgICBnZXQgbWF4X2FnZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fbWF4X2FnZTtcclxuICAgIH1cclxuICAgIGdldCB1aV9sb2NhbGVzKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl91aV9sb2NhbGVzO1xyXG4gICAgfVxyXG4gICAgZ2V0IGFjcl92YWx1ZXMoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2Fjcl92YWx1ZXM7XHJcbiAgICB9XHJcbiAgICBnZXQgcmVzb3VyY2UoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Jlc291cmNlO1xyXG4gICAgfVxyXG4gICAgZ2V0IHJlc3BvbnNlX21vZGUoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Jlc3BvbnNlX21vZGU7XHJcbiAgICB9XHJcblxyXG5cclxuICAgIC8vIG1ldGFkYXRhXHJcbiAgICBnZXQgYXV0aG9yaXR5KCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9hdXRob3JpdHk7XHJcbiAgICB9XHJcbiAgICBzZXQgYXV0aG9yaXR5KHZhbHVlKSB7XHJcbiAgICAgICAgaWYgKCF0aGlzLl9hdXRob3JpdHkpIHtcclxuICAgICAgICAgICAgLy8gb25lLXRpbWUgc2V0IG9ubHlcclxuICAgICAgICAgICAgdGhpcy5fYXV0aG9yaXR5ID0gdmFsdWU7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJPaWRjQ2xpZW50U2V0dGluZ3Muc2V0X2F1dGhvcml0eTogYXV0aG9yaXR5IGhhcyBhbHJlYWR5IGJlZW4gYXNzaWduZWQuXCIpXHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcImF1dGhvcml0eSBoYXMgYWxyZWFkeSBiZWVuIGFzc2lnbmVkLlwiKVxyXG4gICAgICAgIH1cclxuICAgIH1cclxuICAgIGdldCBtZXRhZGF0YVVybCgpIHtcclxuICAgICAgICBpZiAoIXRoaXMuX21ldGFkYXRhVXJsKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX21ldGFkYXRhVXJsID0gdGhpcy5hdXRob3JpdHk7XHJcblxyXG4gICAgICAgICAgICBpZiAodGhpcy5fbWV0YWRhdGFVcmwgJiYgdGhpcy5fbWV0YWRhdGFVcmwuaW5kZXhPZihPaWRjTWV0YWRhdGFVcmxQYXRoKSA8IDApIHtcclxuICAgICAgICAgICAgICAgIGlmICh0aGlzLl9tZXRhZGF0YVVybFt0aGlzLl9tZXRhZGF0YVVybC5sZW5ndGggLSAxXSAhPT0gJy8nKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fbWV0YWRhdGFVcmwgKz0gJy8nO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgdGhpcy5fbWV0YWRhdGFVcmwgKz0gT2lkY01ldGFkYXRhVXJsUGF0aDtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX21ldGFkYXRhVXJsO1xyXG4gICAgfVxyXG5cclxuICAgIC8vIHNldHRhYmxlL2NhY2hhYmxlIG1ldGFkYXRhIHZhbHVlc1xyXG4gICAgZ2V0IG1ldGFkYXRhKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9tZXRhZGF0YTtcclxuICAgIH1cclxuICAgIHNldCBtZXRhZGF0YSh2YWx1ZSkge1xyXG4gICAgICAgIHRoaXMuX21ldGFkYXRhID0gdmFsdWU7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IHNpZ25pbmdLZXlzKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9zaWduaW5nS2V5cztcclxuICAgIH1cclxuICAgIHNldCBzaWduaW5nS2V5cyh2YWx1ZSkge1xyXG4gICAgICAgIHRoaXMuX3NpZ25pbmdLZXlzID0gdmFsdWU7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gYmVoYXZpb3IgZmxhZ3NcclxuICAgIGdldCBmaWx0ZXJQcm90b2NvbENsYWltcygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fZmlsdGVyUHJvdG9jb2xDbGFpbXM7XHJcbiAgICB9XHJcbiAgICBnZXQgbG9hZFVzZXJJbmZvKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9sb2FkVXNlckluZm87XHJcbiAgICB9XHJcbiAgICBnZXQgc3RhbGVTdGF0ZUFnZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc3RhbGVTdGF0ZUFnZTtcclxuICAgIH1cclxuICAgIGdldCBjbG9ja1NrZXcoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2Nsb2NrU2tldztcclxuICAgIH1cclxuICAgIGdldCB1c2VySW5mb0p3dElzc3VlcigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fdXNlckluZm9Kd3RJc3N1ZXI7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IHN0YXRlU3RvcmUoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3N0YXRlU3RvcmU7XHJcbiAgICB9XHJcbiAgICBnZXQgdmFsaWRhdG9yKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl92YWxpZGF0b3I7XHJcbiAgICB9XHJcbiAgICBnZXQgbWV0YWRhdGFTZXJ2aWNlKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9tZXRhZGF0YVNlcnZpY2U7XHJcbiAgICB9XHJcblxyXG4gICAgLy8gZXh0cmEgcXVlcnkgcGFyYW1zXHJcbiAgICBnZXQgZXh0cmFRdWVyeVBhcmFtcygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fZXh0cmFRdWVyeVBhcmFtcztcclxuICAgIH1cclxuICAgIHNldCBleHRyYVF1ZXJ5UGFyYW1zKHZhbHVlKSB7XHJcbiAgICAgICAgaWYgKHR5cGVvZiB2YWx1ZSA9PT0gJ29iamVjdCcpe1xyXG4gICAgICAgICAgICB0aGlzLl9leHRyYVF1ZXJ5UGFyYW1zID0gdmFsdWU7XHJcbiAgICAgICAgfSBlbHNlIHtcclxuICAgICAgICAgICAgdGhpcy5fZXh0cmFRdWVyeVBhcmFtcyA9IHt9O1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICAvLyBleHRyYSB0b2tlbiBwYXJhbXNcclxuICAgIGdldCBleHRyYVRva2VuUGFyYW1zKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9leHRyYVRva2VuUGFyYW1zO1xyXG4gICAgfVxyXG4gICAgc2V0IGV4dHJhVG9rZW5QYXJhbXModmFsdWUpIHtcclxuICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jyl7XHJcbiAgICAgICAgICAgIHRoaXMuX2V4dHJhVG9rZW5QYXJhbXMgPSB2YWx1ZTtcclxuICAgICAgICB9IGVsc2Uge1xyXG4gICAgICAgICAgICB0aGlzLl9leHRyYVRva2VuUGFyYW1zID0ge307XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuaW1wb3J0IHsgUG9wdXBXaW5kb3cgfSBmcm9tICcuL1BvcHVwV2luZG93LmpzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBQb3B1cE5hdmlnYXRvciB7XHJcblxyXG4gICAgcHJlcGFyZShwYXJhbXMpIHtcclxuICAgICAgICBsZXQgcG9wdXAgPSBuZXcgUG9wdXBXaW5kb3cocGFyYW1zKTtcclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHBvcHVwKTtcclxuICAgIH1cclxuXHJcbiAgICBjYWxsYmFjayh1cmwsIGtlZXBPcGVuLCBkZWxpbWl0ZXIpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJQb3B1cE5hdmlnYXRvci5jYWxsYmFja1wiKTtcclxuXHJcbiAgICAgICAgdHJ5IHtcclxuICAgICAgICAgICAgUG9wdXBXaW5kb3cubm90aWZ5T3BlbmVyKHVybCwga2VlcE9wZW4sIGRlbGltaXRlcik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgY2F0Y2ggKGUpIHtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KGUpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcbmltcG9ydCB7IFVybFV0aWxpdHkgfSBmcm9tICcuL1VybFV0aWxpdHkuanMnO1xyXG5cclxuY29uc3QgQ2hlY2tGb3JQb3B1cENsb3NlZEludGVydmFsID0gNTAwO1xyXG5jb25zdCBEZWZhdWx0UG9wdXBGZWF0dXJlcyA9ICdsb2NhdGlvbj1ubyx0b29sYmFyPW5vLHdpZHRoPTUwMCxoZWlnaHQ9NTAwLGxlZnQ9MTAwLHRvcD0xMDA7JztcclxuLy9jb25zdCBEZWZhdWx0UG9wdXBGZWF0dXJlcyA9ICdsb2NhdGlvbj1ubyx0b29sYmFyPW5vLHdpZHRoPTUwMCxoZWlnaHQ9NTAwLGxlZnQ9MTAwLHRvcD0xMDA7cmVzaXphYmxlPXllcyc7XHJcblxyXG5jb25zdCBEZWZhdWx0UG9wdXBUYXJnZXQgPSBcIl9ibGFua1wiO1xyXG5cclxuZXhwb3J0IGNsYXNzIFBvcHVwV2luZG93IHtcclxuXHJcbiAgICBjb25zdHJ1Y3RvcihwYXJhbXMpIHtcclxuICAgICAgICB0aGlzLl9wcm9taXNlID0gbmV3IFByb21pc2UoKHJlc29sdmUsIHJlamVjdCkgPT4ge1xyXG4gICAgICAgICAgICB0aGlzLl9yZXNvbHZlID0gcmVzb2x2ZTtcclxuICAgICAgICAgICAgdGhpcy5fcmVqZWN0ID0gcmVqZWN0O1xyXG4gICAgICAgIH0pO1xyXG5cclxuICAgICAgICBsZXQgdGFyZ2V0ID0gcGFyYW1zLnBvcHVwV2luZG93VGFyZ2V0IHx8IERlZmF1bHRQb3B1cFRhcmdldDtcclxuICAgICAgICBsZXQgZmVhdHVyZXMgPSBwYXJhbXMucG9wdXBXaW5kb3dGZWF0dXJlcyB8fCBEZWZhdWx0UG9wdXBGZWF0dXJlcztcclxuXHJcbiAgICAgICAgdGhpcy5fcG9wdXAgPSB3aW5kb3cub3BlbignJywgdGFyZ2V0LCBmZWF0dXJlcyk7XHJcbiAgICAgICAgaWYgKHRoaXMuX3BvcHVwKSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlBvcHVwV2luZG93LmN0b3I6IHBvcHVwIHN1Y2Nlc3NmdWxseSBjcmVhdGVkXCIpO1xyXG4gICAgICAgICAgICB0aGlzLl9jaGVja0ZvclBvcHVwQ2xvc2VkVGltZXIgPSB3aW5kb3cuc2V0SW50ZXJ2YWwodGhpcy5fY2hlY2tGb3JQb3B1cENsb3NlZC5iaW5kKHRoaXMpLCBDaGVja0ZvclBvcHVwQ2xvc2VkSW50ZXJ2YWwpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBnZXQgcHJvbWlzZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fcHJvbWlzZTtcclxuICAgIH1cclxuXHJcbiAgICBuYXZpZ2F0ZShwYXJhbXMpIHtcclxuICAgICAgICBpZiAoIXRoaXMuX3BvcHVwKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2Vycm9yKFwiUG9wdXBXaW5kb3cubmF2aWdhdGU6IEVycm9yIG9wZW5pbmcgcG9wdXAgd2luZG93XCIpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIGlmICghcGFyYW1zIHx8ICFwYXJhbXMudXJsKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2Vycm9yKFwiUG9wdXBXaW5kb3cubmF2aWdhdGU6IG5vIHVybCBwcm92aWRlZFwiKTtcclxuICAgICAgICAgICAgdGhpcy5fZXJyb3IoXCJObyB1cmwgcHJvdmlkZWRcIik7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJQb3B1cFdpbmRvdy5uYXZpZ2F0ZTogU2V0dGluZyBVUkwgaW4gcG9wdXBcIik7XHJcblxyXG4gICAgICAgICAgICB0aGlzLl9pZCA9IHBhcmFtcy5pZDtcclxuICAgICAgICAgICAgaWYgKHRoaXMuX2lkKSB7XHJcbiAgICAgICAgICAgICAgICB3aW5kb3dbXCJwb3B1cENhbGxiYWNrX1wiICsgcGFyYW1zLmlkXSA9IHRoaXMuX2NhbGxiYWNrLmJpbmQodGhpcyk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHRoaXMuX3BvcHVwLmZvY3VzKCk7XHJcbiAgICAgICAgICAgIHRoaXMuX3BvcHVwLndpbmRvdy5sb2NhdGlvbiA9IHBhcmFtcy51cmw7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5wcm9taXNlO1xyXG4gICAgfVxyXG5cclxuICAgIF9zdWNjZXNzKGRhdGEpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJQb3B1cFdpbmRvdy5jYWxsYmFjazogU3VjY2Vzc2Z1bCByZXNwb25zZSBmcm9tIHBvcHVwIHdpbmRvd1wiKTtcclxuXHJcbiAgICAgICAgdGhpcy5fY2xlYW51cCgpO1xyXG4gICAgICAgIHRoaXMuX3Jlc29sdmUoZGF0YSk7XHJcbiAgICB9XHJcbiAgICBfZXJyb3IobWVzc2FnZSkge1xyXG4gICAgICAgIExvZy5lcnJvcihcIlBvcHVwV2luZG93LmVycm9yOiBcIiwgbWVzc2FnZSk7XHJcbiAgICAgICAgXHJcbiAgICAgICAgdGhpcy5fY2xlYW51cCgpO1xyXG4gICAgICAgIHRoaXMuX3JlamVjdChuZXcgRXJyb3IobWVzc2FnZSkpO1xyXG4gICAgfVxyXG5cclxuICAgIGNsb3NlKCkge1xyXG4gICAgICAgIHRoaXMuX2NsZWFudXAoZmFsc2UpO1xyXG4gICAgfVxyXG5cclxuICAgIF9jbGVhbnVwKGtlZXBPcGVuKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiUG9wdXBXaW5kb3cuY2xlYW51cFwiKTtcclxuXHJcbiAgICAgICAgd2luZG93LmNsZWFySW50ZXJ2YWwodGhpcy5fY2hlY2tGb3JQb3B1cENsb3NlZFRpbWVyKTtcclxuICAgICAgICB0aGlzLl9jaGVja0ZvclBvcHVwQ2xvc2VkVGltZXIgPSBudWxsO1xyXG5cclxuICAgICAgICBkZWxldGUgd2luZG93W1wicG9wdXBDYWxsYmFja19cIiArIHRoaXMuX2lkXTtcclxuXHJcbiAgICAgICAgaWYgKHRoaXMuX3BvcHVwICYmICFrZWVwT3Blbikge1xyXG4gICAgICAgICAgICB0aGlzLl9wb3B1cC5jbG9zZSgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0aGlzLl9wb3B1cCA9IG51bGw7XHJcbiAgICB9XHJcblxyXG4gICAgX2NoZWNrRm9yUG9wdXBDbG9zZWQoKSB7XHJcbiAgICAgICAgaWYgKCF0aGlzLl9wb3B1cCB8fCB0aGlzLl9wb3B1cC5jbG9zZWQpIHtcclxuICAgICAgICAgICAgdGhpcy5fZXJyb3IoXCJQb3B1cCB3aW5kb3cgY2xvc2VkXCIpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBfY2FsbGJhY2sodXJsLCBrZWVwT3Blbikge1xyXG4gICAgICAgIHRoaXMuX2NsZWFudXAoa2VlcE9wZW4pO1xyXG5cclxuICAgICAgICBpZiAodXJsKSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlBvcHVwV2luZG93LmNhbGxiYWNrIHN1Y2Nlc3NcIik7XHJcbiAgICAgICAgICAgIHRoaXMuX3N1Y2Nlc3MoeyB1cmw6IHVybCB9KTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlBvcHVwV2luZG93LmNhbGxiYWNrOiBJbnZhbGlkIHJlc3BvbnNlIGZyb20gcG9wdXBcIik7XHJcbiAgICAgICAgICAgIHRoaXMuX2Vycm9yKFwiSW52YWxpZCByZXNwb25zZSBmcm9tIHBvcHVwXCIpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWMgbm90aWZ5T3BlbmVyKHVybCwga2VlcE9wZW4sIGRlbGltaXRlcikge1xyXG4gICAgICAgIGlmICh3aW5kb3cub3BlbmVyKSB7XHJcbiAgICAgICAgICAgIHVybCA9IHVybCB8fCB3aW5kb3cubG9jYXRpb24uaHJlZjtcclxuICAgICAgICAgICAgaWYgKHVybCkge1xyXG4gICAgICAgICAgICAgICAgdmFyIGRhdGEgPSBVcmxVdGlsaXR5LnBhcnNlVXJsRnJhZ21lbnQodXJsLCBkZWxpbWl0ZXIpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmIChkYXRhLnN0YXRlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgdmFyIG5hbWUgPSBcInBvcHVwQ2FsbGJhY2tfXCIgKyBkYXRhLnN0YXRlO1xyXG4gICAgICAgICAgICAgICAgICAgIHZhciBjYWxsYmFjayA9IHdpbmRvdy5vcGVuZXJbbmFtZV07XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKGNhbGxiYWNrKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlBvcHVwV2luZG93Lm5vdGlmeU9wZW5lcjogcGFzc2luZyB1cmwgbWVzc2FnZSB0byBvcGVuZXJcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGNhbGxiYWNrKHVybCwga2VlcE9wZW4pO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgTG9nLndhcm4oXCJQb3B1cFdpbmRvdy5ub3RpZnlPcGVuZXI6IG5vIG1hdGNoaW5nIGNhbGxiYWNrIGZvdW5kIG9uIG9wZW5lclwiKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cud2FybihcIlBvcHVwV2luZG93Lm5vdGlmeU9wZW5lcjogbm8gc3RhdGUgZm91bmQgaW4gcmVzcG9uc2UgdXJsXCIpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICBMb2cud2FybihcIlBvcHVwV2luZG93Lm5vdGlmeU9wZW5lcjogbm8gd2luZG93Lm9wZW5lci4gQ2FuJ3QgY29tcGxldGUgbm90aWZpY2F0aW9uLlwiKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5cclxuZXhwb3J0IGNsYXNzIFJlZGlyZWN0TmF2aWdhdG9yIHtcclxuXHJcbiAgICBwcmVwYXJlKCkge1xyXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUodGhpcyk7XHJcbiAgICB9XHJcblxyXG4gICAgbmF2aWdhdGUocGFyYW1zKSB7XHJcbiAgICAgICAgaWYgKCFwYXJhbXMgfHwgIXBhcmFtcy51cmwpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVkaXJlY3ROYXZpZ2F0b3IubmF2aWdhdGU6IE5vIHVybCBwcm92aWRlZFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5vIHVybCBwcm92aWRlZFwiKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAocGFyYW1zLnVzZVJlcGxhY2VUb05hdmlnYXRlKSB7XHJcbiAgICAgICAgICAgIHdpbmRvdy5sb2NhdGlvbi5yZXBsYWNlKHBhcmFtcy51cmwpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgd2luZG93LmxvY2F0aW9uID0gcGFyYW1zLnVybDtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgdXJsKCkge1xyXG4gICAgICAgIHJldHVybiB3aW5kb3cubG9jYXRpb24uaHJlZjtcclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcbmltcG9ydCB7IE1ldGFkYXRhU2VydmljZSB9IGZyb20gJy4vTWV0YWRhdGFTZXJ2aWNlLmpzJztcclxuaW1wb3J0IHsgVXNlckluZm9TZXJ2aWNlIH0gZnJvbSAnLi9Vc2VySW5mb1NlcnZpY2UuanMnO1xyXG5pbXBvcnQgeyBUb2tlbkNsaWVudCB9IGZyb20gJy4vVG9rZW5DbGllbnQuanMnO1xyXG5pbXBvcnQgeyBFcnJvclJlc3BvbnNlIH0gZnJvbSAnLi9FcnJvclJlc3BvbnNlLmpzJztcclxuaW1wb3J0IHsgSm9zZVV0aWwgfSBmcm9tICcuL0pvc2VVdGlsLmpzJztcclxuXHJcbmNvbnN0IFByb3RvY29sQ2xhaW1zID0gW1wibm9uY2VcIiwgXCJhdF9oYXNoXCIsIFwiaWF0XCIsIFwibmJmXCIsIFwiZXhwXCIsIFwiYXVkXCIsIFwiaXNzXCIsIFwiY19oYXNoXCJdO1xyXG5cclxuZXhwb3J0IGNsYXNzIFJlc3BvbnNlVmFsaWRhdG9yIHtcclxuXHJcbiAgICBjb25zdHJ1Y3RvcihzZXR0aW5ncywgXHJcbiAgICAgICAgTWV0YWRhdGFTZXJ2aWNlQ3RvciA9IE1ldGFkYXRhU2VydmljZSxcclxuICAgICAgICBVc2VySW5mb1NlcnZpY2VDdG9yID0gVXNlckluZm9TZXJ2aWNlLCBcclxuICAgICAgICBqb3NlVXRpbCA9IEpvc2VVdGlsLFxyXG4gICAgICAgIFRva2VuQ2xpZW50Q3RvciA9IFRva2VuQ2xpZW50KSB7XHJcbiAgICAgICAgaWYgKCFzZXR0aW5ncykge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5jdG9yOiBObyBzZXR0aW5ncyBwYXNzZWQgdG8gUmVzcG9uc2VWYWxpZGF0b3JcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcInNldHRpbmdzXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fc2V0dGluZ3MgPSBzZXR0aW5ncztcclxuICAgICAgICB0aGlzLl9tZXRhZGF0YVNlcnZpY2UgPSBuZXcgTWV0YWRhdGFTZXJ2aWNlQ3Rvcih0aGlzLl9zZXR0aW5ncyk7XHJcbiAgICAgICAgdGhpcy5fdXNlckluZm9TZXJ2aWNlID0gbmV3IFVzZXJJbmZvU2VydmljZUN0b3IodGhpcy5fc2V0dGluZ3MpO1xyXG4gICAgICAgIHRoaXMuX2pvc2VVdGlsID0gam9zZVV0aWw7XHJcbiAgICAgICAgdGhpcy5fdG9rZW5DbGllbnQgPSBuZXcgVG9rZW5DbGllbnRDdG9yKHRoaXMuX3NldHRpbmdzKTtcclxuICAgIH1cclxuXHJcbiAgICB2YWxpZGF0ZVNpZ25pblJlc3BvbnNlKHN0YXRlLCByZXNwb25zZSkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLnZhbGlkYXRlU2lnbmluUmVzcG9uc2VcIik7XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLl9wcm9jZXNzU2lnbmluUGFyYW1zKHN0YXRlLCByZXNwb25zZSkudGhlbihyZXNwb25zZSA9PiB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLnZhbGlkYXRlU2lnbmluUmVzcG9uc2U6IHN0YXRlIHByb2Nlc3NlZFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3ZhbGlkYXRlVG9rZW5zKHN0YXRlLCByZXNwb25zZSkudGhlbihyZXNwb25zZSA9PiB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJSZXNwb25zZVZhbGlkYXRvci52YWxpZGF0ZVNpZ25pblJlc3BvbnNlOiB0b2tlbnMgdmFsaWRhdGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3Byb2Nlc3NDbGFpbXMoc3RhdGUsIHJlc3BvbnNlKS50aGVuKHJlc3BvbnNlID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJSZXNwb25zZVZhbGlkYXRvci52YWxpZGF0ZVNpZ25pblJlc3BvbnNlOiBjbGFpbXMgcHJvY2Vzc2VkXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiByZXNwb25zZTtcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICB2YWxpZGF0ZVNpZ25vdXRSZXNwb25zZShzdGF0ZSwgcmVzcG9uc2UpIHtcclxuICAgICAgICBpZiAoc3RhdGUuaWQgIT09IHJlc3BvbnNlLnN0YXRlKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLnZhbGlkYXRlU2lnbm91dFJlc3BvbnNlOiBTdGF0ZSBkb2VzIG5vdCBtYXRjaFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIlN0YXRlIGRvZXMgbm90IG1hdGNoXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIC8vIG5vdyB0aGF0IHdlIGtub3cgdGhlIHN0YXRlIG1hdGNoZXMsIHRha2UgdGhlIHN0b3JlZCBkYXRhXHJcbiAgICAgICAgLy8gYW5kIHNldCBpdCBpbnRvIHRoZSByZXNwb25zZSBzbyBjYWxsZXJzIGNhbiBnZXQgdGhlaXIgc3RhdGVcclxuICAgICAgICAvLyB0aGlzIGlzIGltcG9ydGFudCBmb3IgYm90aCBzdWNjZXNzICYgZXJyb3Igb3V0Y29tZXNcclxuICAgICAgICBMb2cuZGVidWcoXCJSZXNwb25zZVZhbGlkYXRvci52YWxpZGF0ZVNpZ25vdXRSZXNwb25zZTogc3RhdGUgdmFsaWRhdGVkXCIpO1xyXG4gICAgICAgIHJlc3BvbnNlLnN0YXRlID0gc3RhdGUuZGF0YTtcclxuXHJcbiAgICAgICAgaWYgKHJlc3BvbnNlLmVycm9yKSB7XHJcbiAgICAgICAgICAgIExvZy53YXJuKFwiUmVzcG9uc2VWYWxpZGF0b3IudmFsaWRhdGVTaWdub3V0UmVzcG9uc2U6IFJlc3BvbnNlIHdhcyBlcnJvclwiLCByZXNwb25zZS5lcnJvcik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3JSZXNwb25zZShyZXNwb25zZSkpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICB9XHJcblxyXG4gICAgX3Byb2Nlc3NTaWduaW5QYXJhbXMoc3RhdGUsIHJlc3BvbnNlKSB7XHJcbiAgICAgICAgaWYgKHN0YXRlLmlkICE9PSByZXNwb25zZS5zdGF0ZSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fcHJvY2Vzc1NpZ25pblBhcmFtczogU3RhdGUgZG9lcyBub3QgbWF0Y2hcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJTdGF0ZSBkb2VzIG5vdCBtYXRjaFwiKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoIXN0YXRlLmNsaWVudF9pZCkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fcHJvY2Vzc1NpZ25pblBhcmFtczogTm8gY2xpZW50X2lkIG9uIHN0YXRlXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiTm8gY2xpZW50X2lkIG9uIHN0YXRlXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICghc3RhdGUuYXV0aG9yaXR5KSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl9wcm9jZXNzU2lnbmluUGFyYW1zOiBObyBhdXRob3JpdHkgb24gc3RhdGVcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJObyBhdXRob3JpdHkgb24gc3RhdGVcIikpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gdGhpcyBhbGxvd3MgdGhlIGF1dGhvcml0eSB0byBiZSBsb2FkZWQgZnJvbSB0aGUgc2lnbmluIHN0YXRlXHJcbiAgICAgICAgaWYgKCF0aGlzLl9zZXR0aW5ncy5hdXRob3JpdHkpIHtcclxuICAgICAgICAgICAgdGhpcy5fc2V0dGluZ3MuYXV0aG9yaXR5ID0gc3RhdGUuYXV0aG9yaXR5O1xyXG4gICAgICAgIH1cclxuICAgICAgICAvLyBlbnN1cmUgd2UncmUgdXNpbmcgdGhlIGNvcnJlY3QgYXV0aG9yaXR5IGlmIHRoZSBhdXRob3JpdHkgaXMgbm90IGxvYWRlZCBmcm9tIHNpZ25pbiBzdGF0ZVxyXG4gICAgICAgIGVsc2UgaWYgKHRoaXMuX3NldHRpbmdzLmF1dGhvcml0eSAmJiB0aGlzLl9zZXR0aW5ncy5hdXRob3JpdHkgIT09IHN0YXRlLmF1dGhvcml0eSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fcHJvY2Vzc1NpZ25pblBhcmFtczogYXV0aG9yaXR5IG1pc21hdGNoIG9uIHNldHRpbmdzIHZzLiBzaWduaW4gc3RhdGVcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJhdXRob3JpdHkgbWlzbWF0Y2ggb24gc2V0dGluZ3MgdnMuIHNpZ25pbiBzdGF0ZVwiKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIC8vIHRoaXMgYWxsb3dzIHRoZSBjbGllbnRfaWQgdG8gYmUgbG9hZGVkIGZyb20gdGhlIHNpZ25pbiBzdGF0ZVxyXG4gICAgICAgIGlmICghdGhpcy5fc2V0dGluZ3MuY2xpZW50X2lkKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX3NldHRpbmdzLmNsaWVudF9pZCA9IHN0YXRlLmNsaWVudF9pZDtcclxuICAgICAgICB9XHJcbiAgICAgICAgLy8gZW5zdXJlIHdlJ3JlIHVzaW5nIHRoZSBjb3JyZWN0IGNsaWVudF9pZCBpZiB0aGUgY2xpZW50X2lkIGlzIG5vdCBsb2FkZWQgZnJvbSBzaWduaW4gc3RhdGVcclxuICAgICAgICBlbHNlIGlmICh0aGlzLl9zZXR0aW5ncy5jbGllbnRfaWQgJiYgdGhpcy5fc2V0dGluZ3MuY2xpZW50X2lkICE9PSBzdGF0ZS5jbGllbnRfaWQpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3Byb2Nlc3NTaWduaW5QYXJhbXM6IGNsaWVudF9pZCBtaXNtYXRjaCBvbiBzZXR0aW5ncyB2cy4gc2lnbmluIHN0YXRlXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiY2xpZW50X2lkIG1pc21hdGNoIG9uIHNldHRpbmdzIHZzLiBzaWduaW4gc3RhdGVcIikpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgLy8gbm93IHRoYXQgd2Uga25vdyB0aGUgc3RhdGUgbWF0Y2hlcywgdGFrZSB0aGUgc3RvcmVkIGRhdGFcclxuICAgICAgICAvLyBhbmQgc2V0IGl0IGludG8gdGhlIHJlc3BvbnNlIHNvIGNhbGxlcnMgY2FuIGdldCB0aGVpciBzdGF0ZVxyXG4gICAgICAgIC8vIHRoaXMgaXMgaW1wb3J0YW50IGZvciBib3RoIHN1Y2Nlc3MgJiBlcnJvciBvdXRjb21lc1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl9wcm9jZXNzU2lnbmluUGFyYW1zOiBzdGF0ZSB2YWxpZGF0ZWRcIik7XHJcbiAgICAgICAgcmVzcG9uc2Uuc3RhdGUgPSBzdGF0ZS5kYXRhO1xyXG5cclxuICAgICAgICBpZiAocmVzcG9uc2UuZXJyb3IpIHtcclxuICAgICAgICAgICAgTG9nLndhcm4oXCJSZXNwb25zZVZhbGlkYXRvci5fcHJvY2Vzc1NpZ25pblBhcmFtczogUmVzcG9uc2Ugd2FzIGVycm9yXCIsIHJlc3BvbnNlLmVycm9yKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvclJlc3BvbnNlKHJlc3BvbnNlKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoc3RhdGUubm9uY2UgJiYgIXJlc3BvbnNlLmlkX3Rva2VuKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl9wcm9jZXNzU2lnbmluUGFyYW1zOiBFeHBlY3RpbmcgaWRfdG9rZW4gaW4gcmVzcG9uc2VcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJObyBpZF90b2tlbiBpbiByZXNwb25zZVwiKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoIXN0YXRlLm5vbmNlICYmIHJlc3BvbnNlLmlkX3Rva2VuKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl9wcm9jZXNzU2lnbmluUGFyYW1zOiBOb3QgZXhwZWN0aW5nIGlkX3Rva2VuIGluIHJlc3BvbnNlXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiVW5leHBlY3RlZCBpZF90b2tlbiBpbiByZXNwb25zZVwiKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoc3RhdGUuY29kZV92ZXJpZmllciAmJiAhcmVzcG9uc2UuY29kZSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fcHJvY2Vzc1NpZ25pblBhcmFtczogRXhwZWN0aW5nIGNvZGUgaW4gcmVzcG9uc2VcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJObyBjb2RlIGluIHJlc3BvbnNlXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICghc3RhdGUuY29kZV92ZXJpZmllciAmJiByZXNwb25zZS5jb2RlKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl9wcm9jZXNzU2lnbmluUGFyYW1zOiBOb3QgZXhwZWN0aW5nIGNvZGUgaW4gcmVzcG9uc2VcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJVbmV4cGVjdGVkIGNvZGUgaW4gcmVzcG9uc2VcIikpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKCFyZXNwb25zZS5zY29wZSkge1xyXG4gICAgICAgICAgICAvLyBpZiB0aGVyZSdzIG5vIHNjb3BlIG9uIHRoZSByZXNwb25zZSwgdGhlbiBhc3N1bWUgYWxsIHNjb3BlcyBncmFudGVkIChwZXItc3BlYykgYW5kIGNvcHkgb3ZlciBzY29wZXMgZnJvbSBvcmlnaW5hbCByZXF1ZXN0XHJcbiAgICAgICAgICAgIHJlc3BvbnNlLnNjb3BlID0gc3RhdGUuc2NvcGU7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgIH1cclxuXHJcbiAgICBfcHJvY2Vzc0NsYWltcyhzdGF0ZSwgcmVzcG9uc2UpIHtcclxuICAgICAgICBpZiAocmVzcG9uc2UuaXNPcGVuSWRDb25uZWN0KSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl9wcm9jZXNzQ2xhaW1zOiByZXNwb25zZSBpcyBPSURDLCBwcm9jZXNzaW5nIGNsYWltc1wiKTtcclxuXHJcbiAgICAgICAgICAgIHJlc3BvbnNlLnByb2ZpbGUgPSB0aGlzLl9maWx0ZXJQcm90b2NvbENsYWltcyhyZXNwb25zZS5wcm9maWxlKTtcclxuXHJcbiAgICAgICAgICAgIGlmIChzdGF0ZS5za2lwVXNlckluZm8gIT09IHRydWUgJiYgdGhpcy5fc2V0dGluZ3MubG9hZFVzZXJJbmZvICYmIHJlc3BvbnNlLmFjY2Vzc190b2tlbikge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3Byb2Nlc3NDbGFpbXM6IGxvYWRpbmcgdXNlciBpbmZvXCIpO1xyXG5cclxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl91c2VySW5mb1NlcnZpY2UuZ2V0Q2xhaW1zKHJlc3BvbnNlLmFjY2Vzc190b2tlbikudGhlbihjbGFpbXMgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl9wcm9jZXNzQ2xhaW1zOiB1c2VyIGluZm8gY2xhaW1zIHJlY2VpdmVkIGZyb20gdXNlciBpbmZvIGVuZHBvaW50XCIpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICBpZiAoY2xhaW1zLnN1YiAhPT0gcmVzcG9uc2UucHJvZmlsZS5zdWIpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3Byb2Nlc3NDbGFpbXM6IHN1YiBmcm9tIHVzZXIgaW5mbyBlbmRwb2ludCBkb2VzIG5vdCBtYXRjaCBzdWIgaW4gYWNjZXNzX3Rva2VuXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwic3ViIGZyb20gdXNlciBpbmZvIGVuZHBvaW50IGRvZXMgbm90IG1hdGNoIHN1YiBpbiBhY2Nlc3NfdG9rZW5cIikpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmVzcG9uc2UucHJvZmlsZSA9IHRoaXMuX21lcmdlQ2xhaW1zKHJlc3BvbnNlLnByb2ZpbGUsIGNsYWltcyk7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3Byb2Nlc3NDbGFpbXM6IHVzZXIgaW5mbyBjbGFpbXMgcmVjZWl2ZWQsIHVwZGF0ZWQgcHJvZmlsZTpcIiwgcmVzcG9uc2UucHJvZmlsZSk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiByZXNwb25zZTtcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3Byb2Nlc3NDbGFpbXM6IG5vdCBsb2FkaW5nIHVzZXIgaW5mb1wiKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3Byb2Nlc3NDbGFpbXM6IHJlc3BvbnNlIGlzIG5vdCBPSURDLCBub3QgcHJvY2Vzc2luZyBjbGFpbXNcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgIH1cclxuXHJcbiAgICBfbWVyZ2VDbGFpbXMoY2xhaW1zMSwgY2xhaW1zMikge1xyXG4gICAgICAgIHZhciByZXN1bHQgPSBPYmplY3QuYXNzaWduKHt9LCBjbGFpbXMxKTtcclxuXHJcbiAgICAgICAgZm9yIChsZXQgbmFtZSBpbiBjbGFpbXMyKSB7XHJcbiAgICAgICAgICAgIHZhciB2YWx1ZXMgPSBjbGFpbXMyW25hbWVdO1xyXG4gICAgICAgICAgICBpZiAoIUFycmF5LmlzQXJyYXkodmFsdWVzKSkge1xyXG4gICAgICAgICAgICAgICAgdmFsdWVzID0gW3ZhbHVlc107XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdmFsdWVzLmxlbmd0aDsgaSsrKSB7XHJcbiAgICAgICAgICAgICAgICBsZXQgdmFsdWUgPSB2YWx1ZXNbaV07XHJcbiAgICAgICAgICAgICAgICBpZiAoIXJlc3VsdFtuYW1lXSkge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlc3VsdFtuYW1lXSA9IHZhbHVlO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZWxzZSBpZiAoQXJyYXkuaXNBcnJheShyZXN1bHRbbmFtZV0pKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJlc3VsdFtuYW1lXS5pbmRleE9mKHZhbHVlKSA8IDApIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmVzdWx0W25hbWVdLnB1c2godmFsdWUpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGVsc2UgaWYgKHJlc3VsdFtuYW1lXSAhPT0gdmFsdWUpIHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAodHlwZW9mIHZhbHVlID09PSAnb2JqZWN0Jykge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRbbmFtZV0gPSB0aGlzLl9tZXJnZUNsYWltcyhyZXN1bHRbbmFtZV0sIHZhbHVlKTtcclxuICAgICAgICAgICAgICAgICAgICB9IFxyXG4gICAgICAgICAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXN1bHRbbmFtZV0gPSBbcmVzdWx0W25hbWVdLCB2YWx1ZV07XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gcmVzdWx0O1xyXG4gICAgfVxyXG5cclxuICAgIF9maWx0ZXJQcm90b2NvbENsYWltcyhjbGFpbXMpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJSZXNwb25zZVZhbGlkYXRvci5fZmlsdGVyUHJvdG9jb2xDbGFpbXMsIGluY29taW5nIGNsYWltczpcIiwgY2xhaW1zKTtcclxuXHJcbiAgICAgICAgdmFyIHJlc3VsdCA9IE9iamVjdC5hc3NpZ24oe30sIGNsYWltcyk7XHJcblxyXG4gICAgICAgIGlmICh0aGlzLl9zZXR0aW5ncy5fZmlsdGVyUHJvdG9jb2xDbGFpbXMpIHtcclxuICAgICAgICAgICAgUHJvdG9jb2xDbGFpbXMuZm9yRWFjaCh0eXBlID0+IHtcclxuICAgICAgICAgICAgICAgIGRlbGV0ZSByZXN1bHRbdHlwZV07XHJcbiAgICAgICAgICAgIH0pO1xyXG5cclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX2ZpbHRlclByb3RvY29sQ2xhaW1zOiBwcm90b2NvbCBjbGFpbXMgZmlsdGVyZWRcIiwgcmVzdWx0KTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl9maWx0ZXJQcm90b2NvbENsYWltczogcHJvdG9jb2wgY2xhaW1zIG5vdCBmaWx0ZXJlZFwiKVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHJlc3VsdDtcclxuICAgIH1cclxuXHJcbiAgICBfdmFsaWRhdGVUb2tlbnMoc3RhdGUsIHJlc3BvbnNlKSB7XHJcbiAgICAgICAgaWYgKHJlc3BvbnNlLmNvZGUpIHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlVG9rZW5zOiBWYWxpZGF0aW5nIGNvZGVcIik7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLl9wcm9jZXNzQ29kZShzdGF0ZSwgcmVzcG9uc2UpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKHJlc3BvbnNlLmlkX3Rva2VuKSB7XHJcbiAgICAgICAgICAgIGlmIChyZXNwb25zZS5hY2Nlc3NfdG9rZW4pIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZVRva2VuczogVmFsaWRhdGluZyBpZF90b2tlbiBhbmQgYWNjZXNzX3Rva2VuXCIpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3ZhbGlkYXRlSWRUb2tlbkFuZEFjY2Vzc1Rva2VuKHN0YXRlLCByZXNwb25zZSk7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZVRva2VuczogVmFsaWRhdGluZyBpZF90b2tlblwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3ZhbGlkYXRlSWRUb2tlbihzdGF0ZSwgcmVzcG9uc2UpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlVG9rZW5zOiBObyBjb2RlIHRvIHByb2Nlc3Mgb3IgaWRfdG9rZW4gdG8gdmFsaWRhdGVcIik7XHJcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShyZXNwb25zZSk7XHJcbiAgICB9XHJcblxyXG4gICAgX3Byb2Nlc3NDb2RlKHN0YXRlLCByZXNwb25zZSkge1xyXG4gICAgICAgIHZhciByZXF1ZXN0ID0ge1xyXG4gICAgICAgICAgICBjbGllbnRfaWQ6IHN0YXRlLmNsaWVudF9pZCxcclxuICAgICAgICAgICAgY2xpZW50X3NlY3JldDogc3RhdGUuY2xpZW50X3NlY3JldCxcclxuICAgICAgICAgICAgY29kZSA6IHJlc3BvbnNlLmNvZGUsXHJcbiAgICAgICAgICAgIHJlZGlyZWN0X3VyaTogc3RhdGUucmVkaXJlY3RfdXJpLFxyXG4gICAgICAgICAgICBjb2RlX3ZlcmlmaWVyOiBzdGF0ZS5jb2RlX3ZlcmlmaWVyXHJcbiAgICAgICAgfTtcclxuXHJcbiAgICAgICAgaWYgKHN0YXRlLmV4dHJhVG9rZW5QYXJhbXMgJiYgdHlwZW9mKHN0YXRlLmV4dHJhVG9rZW5QYXJhbXMpID09PSAnb2JqZWN0Jykge1xyXG4gICAgICAgICAgICBPYmplY3QuYXNzaWduKHJlcXVlc3QsIHN0YXRlLmV4dHJhVG9rZW5QYXJhbXMpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBcclxuICAgICAgICByZXR1cm4gdGhpcy5fdG9rZW5DbGllbnQuZXhjaGFuZ2VDb2RlKHJlcXVlc3QpLnRoZW4odG9rZW5SZXNwb25zZSA9PiB7XHJcbiAgICAgICAgICAgIFxyXG4gICAgICAgICAgICBmb3IodmFyIGtleSBpbiB0b2tlblJlc3BvbnNlKSB7XHJcbiAgICAgICAgICAgICAgICByZXNwb25zZVtrZXldID0gdG9rZW5SZXNwb25zZVtrZXldO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICBpZiAocmVzcG9uc2UuaWRfdG9rZW4pIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl9wcm9jZXNzQ29kZTogdG9rZW4gcmVzcG9uc2Ugc3VjY2Vzc2Z1bCwgcHJvY2Vzc2luZyBpZF90b2tlblwiKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLl92YWxpZGF0ZUlkVG9rZW5BdHRyaWJ1dGVzKHN0YXRlLCByZXNwb25zZSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJSZXNwb25zZVZhbGlkYXRvci5fcHJvY2Vzc0NvZGU6IHRva2VuIHJlc3BvbnNlIHN1Y2Nlc3NmdWwsIHJldHVybmluZyByZXNwb25zZVwiKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIF92YWxpZGF0ZUlkVG9rZW5BdHRyaWJ1dGVzKHN0YXRlLCByZXNwb25zZSkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9tZXRhZGF0YVNlcnZpY2UuZ2V0SXNzdWVyKCkudGhlbihpc3N1ZXIgPT4ge1xyXG5cclxuICAgICAgICAgICAgbGV0IGF1ZGllbmNlID0gc3RhdGUuY2xpZW50X2lkO1xyXG4gICAgICAgICAgICBsZXQgY2xvY2tTa2V3SW5TZWNvbmRzID0gdGhpcy5fc2V0dGluZ3MuY2xvY2tTa2V3O1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJSZXNwb25zZVZhbGlkYXRvci5fdmFsaWRhdGVJZFRva2VuQXR0cmlidXRlczogVmFsaWRhaW5nIEpXVCBhdHRyaWJ1dGVzOyB1c2luZyBjbG9jayBza2V3IChpbiBzZWNvbmRzKSBvZjogXCIsIGNsb2NrU2tld0luU2Vjb25kcyk7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fam9zZVV0aWwudmFsaWRhdGVKd3RBdHRyaWJ1dGVzKHJlc3BvbnNlLmlkX3Rva2VuLCBpc3N1ZXIsIGF1ZGllbmNlLCBjbG9ja1NrZXdJblNlY29uZHMpLnRoZW4ocGF5bG9hZCA9PiB7XHJcbiAgICAgICAgICAgIFxyXG4gICAgICAgICAgICAgICAgaWYgKHN0YXRlLm5vbmNlICYmIHN0YXRlLm5vbmNlICE9PSBwYXlsb2FkLm5vbmNlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlSWRUb2tlbkF0dHJpYnV0ZXM6IEludmFsaWQgbm9uY2UgaW4gaWRfdG9rZW5cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIkludmFsaWQgbm9uY2UgaW4gaWRfdG9rZW5cIikpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIGlmICghcGF5bG9hZC5zdWIpIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fdmFsaWRhdGVJZFRva2VuQXR0cmlidXRlczogTm8gc3ViIHByZXNlbnQgaW4gaWRfdG9rZW5cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5vIHN1YiBwcmVzZW50IGluIGlkX3Rva2VuXCIpKTtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICByZXNwb25zZS5wcm9maWxlID0gcGF5bG9hZDtcclxuICAgICAgICAgICAgICAgIHJldHVybiByZXNwb25zZTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgX3ZhbGlkYXRlSWRUb2tlbkFuZEFjY2Vzc1Rva2VuKHN0YXRlLCByZXNwb25zZSkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl92YWxpZGF0ZUlkVG9rZW4oc3RhdGUsIHJlc3BvbnNlKS50aGVuKHJlc3BvbnNlID0+IHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3ZhbGlkYXRlQWNjZXNzVG9rZW4ocmVzcG9uc2UpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIF92YWxpZGF0ZUlkVG9rZW4oc3RhdGUsIHJlc3BvbnNlKSB7XHJcbiAgICAgICAgaWYgKCFzdGF0ZS5ub25jZSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fdmFsaWRhdGVJZFRva2VuOiBObyBub25jZSBvbiBzdGF0ZVwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5vIG5vbmNlIG9uIHN0YXRlXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGxldCBqd3QgPSB0aGlzLl9qb3NlVXRpbC5wYXJzZUp3dChyZXNwb25zZS5pZF90b2tlbik7XHJcbiAgICAgICAgaWYgKCFqd3QgfHwgIWp3dC5oZWFkZXIgfHwgIWp3dC5wYXlsb2FkKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZUlkVG9rZW46IEZhaWxlZCB0byBwYXJzZSBpZF90b2tlblwiLCBqd3QpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiRmFpbGVkIHRvIHBhcnNlIGlkX3Rva2VuXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmIChzdGF0ZS5ub25jZSAhPT0gand0LnBheWxvYWQubm9uY2UpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlSWRUb2tlbjogSW52YWxpZCBub25jZSBpbiBpZF90b2tlblwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIkludmFsaWQgbm9uY2UgaW4gaWRfdG9rZW5cIikpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdmFyIGtpZCA9IGp3dC5oZWFkZXIua2lkO1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fbWV0YWRhdGFTZXJ2aWNlLmdldElzc3VlcigpLnRoZW4oaXNzdWVyID0+IHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlSWRUb2tlbjogUmVjZWl2ZWQgaXNzdWVyXCIpO1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX21ldGFkYXRhU2VydmljZS5nZXRTaWduaW5nS2V5cygpLnRoZW4oa2V5cyA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZiAoIWtleXMpIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fdmFsaWRhdGVJZFRva2VuOiBObyBzaWduaW5nIGtleXMgZnJvbSBtZXRhZGF0YVwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiTm8gc2lnbmluZyBrZXlzIGZyb20gbWV0YWRhdGFcIikpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZUlkVG9rZW46IFJlY2VpdmVkIHNpZ25pbmcga2V5c1wiKTtcclxuICAgICAgICAgICAgICAgIGxldCBrZXk7XHJcbiAgICAgICAgICAgICAgICBpZiAoIWtpZCkge1xyXG4gICAgICAgICAgICAgICAgICAgIGtleXMgPSB0aGlzLl9maWx0ZXJCeUFsZyhrZXlzLCBqd3QuaGVhZGVyLmFsZyk7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGlmIChrZXlzLmxlbmd0aCA+IDEpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlSWRUb2tlbjogTm8ga2lkIGZvdW5kIGluIGlkX3Rva2VuIGFuZCBtb3JlIHRoYW4gb25lIGtleSBmb3VuZCBpbiBtZXRhZGF0YVwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5vIGtpZCBmb3VuZCBpbiBpZF90b2tlbiBhbmQgbW9yZSB0aGFuIG9uZSBrZXkgZm91bmQgaW4gbWV0YWRhdGFcIikpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgLy8ga2lkIGlzIG1hbmRhdG9yeSBvbmx5IHdoZW4gdGhlcmUgYXJlIG11bHRpcGxlIGtleXMgaW4gdGhlIHJlZmVyZW5jZWQgSldLIFNldCBkb2N1bWVudFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAvLyBzZWUgaHR0cDovL29wZW5pZC5uZXQvc3BlY3Mvb3BlbmlkLWNvbm5lY3QtY29yZS0xXzAuaHRtbCNTaWduaW5nXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGtleSA9IGtleXNbMF07XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAga2V5ID0ga2V5cy5maWx0ZXIoa2V5ID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGtleS5raWQgPT09IGtpZDtcclxuICAgICAgICAgICAgICAgICAgICB9KVswXTtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBpZiAoIWtleSkge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZUlkVG9rZW46IE5vIGtleSBtYXRjaGluZyBraWQgb3IgYWxnIGZvdW5kIGluIHNpZ25pbmcga2V5c1wiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiTm8ga2V5IG1hdGNoaW5nIGtpZCBvciBhbGcgZm91bmQgaW4gc2lnbmluZyBrZXlzXCIpKTtcclxuICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICBsZXQgYXVkaWVuY2UgPSBzdGF0ZS5jbGllbnRfaWQ7XHJcblxyXG4gICAgICAgICAgICAgICAgbGV0IGNsb2NrU2tld0luU2Vjb25kcyA9IHRoaXMuX3NldHRpbmdzLmNsb2NrU2tldztcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZUlkVG9rZW46IFZhbGlkYWluZyBKV1Q7IHVzaW5nIGNsb2NrIHNrZXcgKGluIHNlY29uZHMpIG9mOiBcIiwgY2xvY2tTa2V3SW5TZWNvbmRzKTtcclxuXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fam9zZVV0aWwudmFsaWRhdGVKd3QocmVzcG9uc2UuaWRfdG9rZW4sIGtleSwgaXNzdWVyLCBhdWRpZW5jZSwgY2xvY2tTa2V3SW5TZWNvbmRzKS50aGVuKCgpPT57XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlSWRUb2tlbjogSldUIHZhbGlkYXRpb24gc3VjY2Vzc2Z1bFwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFqd3QucGF5bG9hZC5zdWIpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlSWRUb2tlbjogTm8gc3ViIHByZXNlbnQgaW4gaWRfdG9rZW5cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJObyBzdWIgcHJlc2VudCBpbiBpZF90b2tlblwiKSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXNwb25zZS5wcm9maWxlID0gand0LnBheWxvYWQ7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiByZXNwb25zZTtcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBfZmlsdGVyQnlBbGcoa2V5cywgYWxnKXtcclxuICAgICAgICB2YXIga3R5ID0gbnVsbDtcclxuICAgICAgICBpZiAoYWxnLnN0YXJ0c1dpdGgoXCJSU1wiKSkge1xyXG4gICAgICAgICAgICBrdHkgPSBcIlJTQVwiO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIGlmIChhbGcuc3RhcnRzV2l0aChcIlBTXCIpKSB7XHJcbiAgICAgICAgICAgIGt0eSA9IFwiUFNcIjtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSBpZiAoYWxnLnN0YXJ0c1dpdGgoXCJFU1wiKSkge1xyXG4gICAgICAgICAgICBrdHkgPSBcIkVDXCI7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJSZXNwb25zZVZhbGlkYXRvci5fZmlsdGVyQnlBbGc6IGFsZyBub3Qgc3VwcG9ydGVkOiBcIiwgYWxnKTtcclxuICAgICAgICAgICAgcmV0dXJuIFtdO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgTG9nLmRlYnVnKFwiUmVzcG9uc2VWYWxpZGF0b3IuX2ZpbHRlckJ5QWxnOiBMb29raW5nIGZvciBrZXlzIHRoYXQgbWF0Y2gga3R5OiBcIiwga3R5KTtcclxuXHJcbiAgICAgICAga2V5cyA9IGtleXMuZmlsdGVyKGtleSA9PiB7XHJcbiAgICAgICAgICAgIHJldHVybiBrZXkua3R5ID09PSBrdHk7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl9maWx0ZXJCeUFsZzogTnVtYmVyIG9mIGtleXMgdGhhdCBtYXRjaCBrdHk6IFwiLCBrdHksIGtleXMubGVuZ3RoKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIGtleXM7XHJcbiAgICB9XHJcblxyXG4gICAgX3ZhbGlkYXRlQWNjZXNzVG9rZW4ocmVzcG9uc2UpIHtcclxuICAgICAgICBpZiAoIXJlc3BvbnNlLnByb2ZpbGUpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlQWNjZXNzVG9rZW46IE5vIHByb2ZpbGUgbG9hZGVkIGZyb20gaWRfdG9rZW5cIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJObyBwcm9maWxlIGxvYWRlZCBmcm9tIGlkX3Rva2VuXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICghcmVzcG9uc2UucHJvZmlsZS5hdF9oYXNoKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZUFjY2Vzc1Rva2VuOiBObyBhdF9oYXNoIGluIGlkX3Rva2VuXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiTm8gYXRfaGFzaCBpbiBpZF90b2tlblwiKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAoIXJlc3BvbnNlLmlkX3Rva2VuKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZUFjY2Vzc1Rva2VuOiBObyBpZF90b2tlblwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5vIGlkX3Rva2VuXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGxldCBqd3QgPSB0aGlzLl9qb3NlVXRpbC5wYXJzZUp3dChyZXNwb25zZS5pZF90b2tlbik7XHJcbiAgICAgICAgaWYgKCFqd3QgfHwgIWp3dC5oZWFkZXIpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlQWNjZXNzVG9rZW46IEZhaWxlZCB0byBwYXJzZSBpZF90b2tlblwiLCBqd3QpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiRmFpbGVkIHRvIHBhcnNlIGlkX3Rva2VuXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHZhciBoYXNoQWxnID0gand0LmhlYWRlci5hbGc7XHJcbiAgICAgICAgaWYgKCFoYXNoQWxnIHx8IGhhc2hBbGcubGVuZ3RoICE9PSA1KSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZUFjY2Vzc1Rva2VuOiBVbnN1cHBvcnRlZCBhbGc6XCIsIGhhc2hBbGcpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiVW5zdXBwb3J0ZWQgYWxnOiBcIiArIGhhc2hBbGcpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHZhciBoYXNoQml0cyA9IGhhc2hBbGcuc3Vic3RyKDIsIDMpO1xyXG4gICAgICAgIGlmICghaGFzaEJpdHMpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlQWNjZXNzVG9rZW46IFVuc3VwcG9ydGVkIGFsZzpcIiwgaGFzaEFsZywgaGFzaEJpdHMpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiVW5zdXBwb3J0ZWQgYWxnOiBcIiArIGhhc2hBbGcpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGhhc2hCaXRzID0gcGFyc2VJbnQoaGFzaEJpdHMpO1xyXG4gICAgICAgIGlmIChoYXNoQml0cyAhPT0gMjU2ICYmIGhhc2hCaXRzICE9PSAzODQgJiYgaGFzaEJpdHMgIT09IDUxMikge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fdmFsaWRhdGVBY2Nlc3NUb2tlbjogVW5zdXBwb3J0ZWQgYWxnOlwiLCBoYXNoQWxnLCBoYXNoQml0cyk7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJVbnN1cHBvcnRlZCBhbGc6IFwiICsgaGFzaEFsZykpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgbGV0IHNoYSA9IFwic2hhXCIgKyBoYXNoQml0cztcclxuICAgICAgICB2YXIgaGFzaCA9IHRoaXMuX2pvc2VVdGlsLmhhc2hTdHJpbmcocmVzcG9uc2UuYWNjZXNzX3Rva2VuLCBzaGEpO1xyXG4gICAgICAgIGlmICghaGFzaCkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJSZXNwb25zZVZhbGlkYXRvci5fdmFsaWRhdGVBY2Nlc3NUb2tlbjogYWNjZXNzX3Rva2VuIGhhc2ggZmFpbGVkOlwiLCBzaGEpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiRmFpbGVkIHRvIHZhbGlkYXRlIGF0X2hhc2hcIikpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdmFyIGxlZnQgPSBoYXNoLnN1YnN0cigwLCBoYXNoLmxlbmd0aCAvIDIpO1xyXG4gICAgICAgIHZhciBsZWZ0X2I2NHUgPSB0aGlzLl9qb3NlVXRpbC5oZXhUb0Jhc2U2NFVybChsZWZ0KTtcclxuICAgICAgICBpZiAobGVmdF9iNjR1ICE9PSByZXNwb25zZS5wcm9maWxlLmF0X2hhc2gpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiUmVzcG9uc2VWYWxpZGF0b3IuX3ZhbGlkYXRlQWNjZXNzVG9rZW46IEZhaWxlZCB0byB2YWxpZGF0ZSBhdF9oYXNoXCIsIGxlZnRfYjY0dSwgcmVzcG9uc2UucHJvZmlsZS5hdF9oYXNoKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIkZhaWxlZCB0byB2YWxpZGF0ZSBhdF9oYXNoXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlJlc3BvbnNlVmFsaWRhdG9yLl92YWxpZGF0ZUFjY2Vzc1Rva2VuOiBzdWNjZXNzXCIpO1xyXG5cclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKHJlc3BvbnNlKTtcclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcbmltcG9ydCB7IENoZWNrU2Vzc2lvbklGcmFtZSB9IGZyb20gJy4vQ2hlY2tTZXNzaW9uSUZyYW1lLmpzJztcclxuaW1wb3J0IHsgR2xvYmFsIH0gZnJvbSAnLi9HbG9iYWwuanMnO1xyXG5cclxuZXhwb3J0IGNsYXNzIFNlc3Npb25Nb25pdG9yIHtcclxuXHJcbiAgICBjb25zdHJ1Y3Rvcih1c2VyTWFuYWdlciwgQ2hlY2tTZXNzaW9uSUZyYW1lQ3RvciA9IENoZWNrU2Vzc2lvbklGcmFtZSwgdGltZXIgPSBHbG9iYWwudGltZXIpIHtcclxuICAgICAgICBpZiAoIXVzZXJNYW5hZ2VyKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlNlc3Npb25Nb25pdG9yLmN0b3I6IE5vIHVzZXIgbWFuYWdlciBwYXNzZWQgdG8gU2Vzc2lvbk1vbml0b3JcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcInVzZXJNYW5hZ2VyXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fdXNlck1hbmFnZXIgPSB1c2VyTWFuYWdlcjtcclxuICAgICAgICB0aGlzLl9DaGVja1Nlc3Npb25JRnJhbWVDdG9yID0gQ2hlY2tTZXNzaW9uSUZyYW1lQ3RvcjtcclxuICAgICAgICB0aGlzLl90aW1lciA9IHRpbWVyO1xyXG5cclxuICAgICAgICB0aGlzLl91c2VyTWFuYWdlci5ldmVudHMuYWRkVXNlckxvYWRlZCh0aGlzLl9zdGFydC5iaW5kKHRoaXMpKTtcclxuICAgICAgICB0aGlzLl91c2VyTWFuYWdlci5ldmVudHMuYWRkVXNlclVubG9hZGVkKHRoaXMuX3N0b3AuYmluZCh0aGlzKSk7XHJcblxyXG4gICAgICAgIHRoaXMuX3VzZXJNYW5hZ2VyLmdldFVzZXIoKS50aGVuKHVzZXIgPT4ge1xyXG4gICAgICAgICAgICAvLyBkb2luZyB0aGlzIG1hbnVhbGx5IGhlcmUgc2luY2UgY2FsbGluZyBnZXRVc2VyIFxyXG4gICAgICAgICAgICAvLyBkb2Vzbid0IHRyaWdnZXIgbG9hZCBldmVudC5cclxuICAgICAgICAgICAgaWYgKHVzZXIpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuX3N0YXJ0KHVzZXIpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2UgaWYgKHRoaXMuX3NldHRpbmdzLm1vbml0b3JBbm9ueW1vdXNTZXNzaW9uKSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl91c2VyTWFuYWdlci5xdWVyeVNlc3Npb25TdGF0dXMoKS50aGVuKHNlc3Npb24gPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIGxldCB0bXBVc2VyID0ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBzZXNzaW9uX3N0YXRlIDogc2Vzc2lvbi5zZXNzaW9uX3N0YXRlXHJcbiAgICAgICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgICAgICAgICBpZiAoc2Vzc2lvbi5zdWIgJiYgc2Vzc2lvbi5zaWQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdG1wVXNlci5wcm9maWxlID0ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc3ViOiBzZXNzaW9uLnN1YixcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHNpZDogc2Vzc2lvbi5zaWRcclxuICAgICAgICAgICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgdGhpcy5fc3RhcnQodG1wVXNlcik7XHJcbiAgICAgICAgICAgICAgICB9KVxyXG4gICAgICAgICAgICAgICAgLmNhdGNoKGVyciA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgLy8gY2F0Y2ggdG8gc3VwcHJlc3MgZXJyb3JzIHNpbmNlIHdlJ3JlIGluIGEgY3RvclxyXG4gICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlNlc3Npb25Nb25pdG9yIGN0b3I6IGVycm9yIGZyb20gcXVlcnlTZXNzaW9uU3RhdHVzOlwiLCBlcnIubWVzc2FnZSk7XHJcbiAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pLmNhdGNoKGVyciA9PiB7XHJcbiAgICAgICAgICAgIC8vIGNhdGNoIHRvIHN1cHByZXNzIGVycm9ycyBzaW5jZSB3ZSdyZSBpbiBhIGN0b3JcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiU2Vzc2lvbk1vbml0b3IgY3RvcjogZXJyb3IgZnJvbSBnZXRVc2VyOlwiLCBlcnIubWVzc2FnZSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IF9zZXR0aW5ncygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fdXNlck1hbmFnZXIuc2V0dGluZ3M7XHJcbiAgICB9XHJcbiAgICBnZXQgX21ldGFkYXRhU2VydmljZSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fdXNlck1hbmFnZXIubWV0YWRhdGFTZXJ2aWNlO1xyXG4gICAgfVxyXG4gICAgZ2V0IF9jbGllbnRfaWQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3NldHRpbmdzLmNsaWVudF9pZDtcclxuICAgIH1cclxuICAgIGdldCBfY2hlY2tTZXNzaW9uSW50ZXJ2YWwoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3NldHRpbmdzLmNoZWNrU2Vzc2lvbkludGVydmFsO1xyXG4gICAgfVxyXG4gICAgZ2V0IF9zdG9wQ2hlY2tTZXNzaW9uT25FcnJvcigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc2V0dGluZ3Muc3RvcENoZWNrU2Vzc2lvbk9uRXJyb3I7XHJcbiAgICB9XHJcblxyXG4gICAgX3N0YXJ0KHVzZXIpIHtcclxuICAgICAgICBsZXQgc2Vzc2lvbl9zdGF0ZSA9IHVzZXIuc2Vzc2lvbl9zdGF0ZTtcclxuXHJcbiAgICAgICAgaWYgKHNlc3Npb25fc3RhdGUpIHtcclxuICAgICAgICAgICAgaWYgKHVzZXIucHJvZmlsZSkge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fc3ViID0gdXNlci5wcm9maWxlLnN1YjtcclxuICAgICAgICAgICAgICAgIHRoaXMuX3NpZCA9IHVzZXIucHJvZmlsZS5zaWQ7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJTZXNzaW9uTW9uaXRvci5fc3RhcnQ6IHNlc3Npb25fc3RhdGU6XCIsIHNlc3Npb25fc3RhdGUsIFwiLCBzdWI6XCIsIHRoaXMuX3N1Yik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9zdWIgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgICAgICAgICB0aGlzLl9zaWQgPSB1bmRlZmluZWQ7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJTZXNzaW9uTW9uaXRvci5fc3RhcnQ6IHNlc3Npb25fc3RhdGU6XCIsIHNlc3Npb25fc3RhdGUsIFwiLCBhbm9ueW1vdXMgdXNlclwiKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgaWYgKCF0aGlzLl9jaGVja1Nlc3Npb25JRnJhbWUpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuX21ldGFkYXRhU2VydmljZS5nZXRDaGVja1Nlc3Npb25JZnJhbWUoKS50aGVuKHVybCA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHVybCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJTZXNzaW9uTW9uaXRvci5fc3RhcnQ6IEluaXRpYWxpemluZyBjaGVjayBzZXNzaW9uIGlmcmFtZVwiKVxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGNsaWVudF9pZCA9IHRoaXMuX2NsaWVudF9pZDtcclxuICAgICAgICAgICAgICAgICAgICAgICAgbGV0IGludGVydmFsID0gdGhpcy5fY2hlY2tTZXNzaW9uSW50ZXJ2YWw7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGxldCBzdG9wT25FcnJvciA9IHRoaXMuX3N0b3BDaGVja1Nlc3Npb25PbkVycm9yO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fY2hlY2tTZXNzaW9uSUZyYW1lID0gbmV3IHRoaXMuX0NoZWNrU2Vzc2lvbklGcmFtZUN0b3IodGhpcy5fY2FsbGJhY2suYmluZCh0aGlzKSwgY2xpZW50X2lkLCB1cmwsIGludGVydmFsLCBzdG9wT25FcnJvcik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX2NoZWNrU2Vzc2lvbklGcmFtZS5sb2FkKCkudGhlbigoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB0aGlzLl9jaGVja1Nlc3Npb25JRnJhbWUuc3RhcnQoc2Vzc2lvbl9zdGF0ZSk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgTG9nLndhcm4oXCJTZXNzaW9uTW9uaXRvci5fc3RhcnQ6IE5vIGNoZWNrIHNlc3Npb24gaWZyYW1lIGZvdW5kIGluIHRoZSBtZXRhZGF0YVwiKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9KS5jYXRjaChlcnIgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgIC8vIGNhdGNoIHRvIHN1cHByZXNzIGVycm9ycyBzaW5jZSB3ZSdyZSBpbiBub24tcHJvbWlzZSBjYWxsYmFja1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlNlc3Npb25Nb25pdG9yLl9zdGFydDogRXJyb3IgZnJvbSBnZXRDaGVja1Nlc3Npb25JZnJhbWU6XCIsIGVyci5tZXNzYWdlKTtcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fY2hlY2tTZXNzaW9uSUZyYW1lLnN0YXJ0KHNlc3Npb25fc3RhdGUpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIF9zdG9wKCkge1xyXG4gICAgICAgIHRoaXMuX3N1YiA9IHVuZGVmaW5lZDtcclxuICAgICAgICB0aGlzLl9zaWQgPSB1bmRlZmluZWQ7XHJcblxyXG4gICAgICAgIGlmICh0aGlzLl9jaGVja1Nlc3Npb25JRnJhbWUpIHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiU2Vzc2lvbk1vbml0b3IuX3N0b3BcIik7XHJcbiAgICAgICAgICAgIHRoaXMuX2NoZWNrU2Vzc2lvbklGcmFtZS5zdG9wKCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAodGhpcy5fc2V0dGluZ3MubW9uaXRvckFub255bW91c1Nlc3Npb24pIHtcclxuICAgICAgICAgICAgLy8gdXNpbmcgYSB0aW1lciB0byBkZWxheSByZS1pbml0aWFsaXphdGlvbiB0byBhdm9pZCByYWNlIGNvbmRpdGlvbnMgZHVyaW5nIHNpZ25vdXRcclxuICAgICAgICAgICAgbGV0IHRpbWVySGFuZGxlID0gdGhpcy5fdGltZXIuc2V0SW50ZXJ2YWwoKCk9PntcclxuICAgICAgICAgICAgICAgIHRoaXMuX3RpbWVyLmNsZWFySW50ZXJ2YWwodGltZXJIYW5kbGUpO1xyXG5cclxuICAgICAgICAgICAgICAgIHRoaXMuX3VzZXJNYW5hZ2VyLnF1ZXJ5U2Vzc2lvblN0YXR1cygpLnRoZW4oc2Vzc2lvbiA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgbGV0IHRtcFVzZXIgPSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlc3Npb25fc3RhdGUgOiBzZXNzaW9uLnNlc3Npb25fc3RhdGVcclxuICAgICAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChzZXNzaW9uLnN1YiAmJiBzZXNzaW9uLnNpZCkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB0bXBVc2VyLnByb2ZpbGUgPSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBzdWI6IHNlc3Npb24uc3ViLFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2lkOiBzZXNzaW9uLnNpZFxyXG4gICAgICAgICAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9zdGFydCh0bXBVc2VyKTtcclxuICAgICAgICAgICAgICAgIH0pXHJcbiAgICAgICAgICAgICAgICAuY2F0Y2goZXJyID0+IHtcclxuICAgICAgICAgICAgICAgICAgICAvLyBjYXRjaCB0byBzdXBwcmVzcyBlcnJvcnMgc2luY2Ugd2UncmUgaW4gYSBjYWxsYmFja1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlNlc3Npb25Nb25pdG9yOiBlcnJvciBmcm9tIHF1ZXJ5U2Vzc2lvblN0YXR1czpcIiwgZXJyLm1lc3NhZ2UpO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICB9LCAxMDAwKTtcclxuICAgICAgICB9XHJcbiAgICB9XHJcblxyXG4gICAgX2NhbGxiYWNrKCkge1xyXG4gICAgICAgIHRoaXMuX3VzZXJNYW5hZ2VyLnF1ZXJ5U2Vzc2lvblN0YXR1cygpLnRoZW4oc2Vzc2lvbiA9PiB7XHJcbiAgICAgICAgICAgIHZhciByYWlzZUV2ZW50ID0gdHJ1ZTtcclxuXHJcbiAgICAgICAgICAgIGlmIChzZXNzaW9uKSB7XHJcbiAgICAgICAgICAgICAgICBpZiAoc2Vzc2lvbi5zdWIgPT09IHRoaXMuX3N1Yikge1xyXG4gICAgICAgICAgICAgICAgICAgIHJhaXNlRXZlbnQgPSBmYWxzZTtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl9jaGVja1Nlc3Npb25JRnJhbWUuc3RhcnQoc2Vzc2lvbi5zZXNzaW9uX3N0YXRlKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHNlc3Npb24uc2lkID09PSB0aGlzLl9zaWQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiU2Vzc2lvbk1vbml0b3IuX2NhbGxiYWNrOiBTYW1lIHN1YiBzdGlsbCBsb2dnZWQgaW4gYXQgT1AsIHJlc3RhcnRpbmcgY2hlY2sgc2Vzc2lvbiBpZnJhbWU7IHNlc3Npb25fc3RhdGU6XCIsIHNlc3Npb24uc2Vzc2lvbl9zdGF0ZSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJTZXNzaW9uTW9uaXRvci5fY2FsbGJhY2s6IFNhbWUgc3ViIHN0aWxsIGxvZ2dlZCBpbiBhdCBPUCwgc2Vzc2lvbiBzdGF0ZSBoYXMgY2hhbmdlZCwgcmVzdGFydGluZyBjaGVjayBzZXNzaW9uIGlmcmFtZTsgc2Vzc2lvbl9zdGF0ZTpcIiwgc2Vzc2lvbi5zZXNzaW9uX3N0YXRlKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fdXNlck1hbmFnZXIuZXZlbnRzLl9yYWlzZVVzZXJTZXNzaW9uQ2hhbmdlZCgpO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlNlc3Npb25Nb25pdG9yLl9jYWxsYmFjazogRGlmZmVyZW50IHN1YmplY3Qgc2lnbmVkIGludG8gT1A6XCIsIHNlc3Npb24uc3ViKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlNlc3Npb25Nb25pdG9yLl9jYWxsYmFjazogU3ViamVjdCBubyBsb25nZXIgc2lnbmVkIGludG8gT1BcIik7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIGlmIChyYWlzZUV2ZW50KSB7XHJcbiAgICAgICAgICAgICAgICBpZiAodGhpcy5fc3ViKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiU2Vzc2lvbk1vbml0b3IuX2NhbGxiYWNrOiBTZXNzaW9uTW9uaXRvci5fY2FsbGJhY2s7IHJhaXNpbmcgc2lnbmVkIG91dCBldmVudFwiKTtcclxuICAgICAgICAgICAgICAgICAgICB0aGlzLl91c2VyTWFuYWdlci5ldmVudHMuX3JhaXNlVXNlclNpZ25lZE91dCgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiU2Vzc2lvbk1vbml0b3IuX2NhbGxiYWNrOiBTZXNzaW9uTW9uaXRvci5fY2FsbGJhY2s7IHJhaXNpbmcgc2lnbmVkIGluIGV2ZW50XCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHRoaXMuX3VzZXJNYW5hZ2VyLmV2ZW50cy5fcmFpc2VVc2VyU2lnbmVkSW4oKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pLmNhdGNoKGVyciA9PiB7XHJcbiAgICAgICAgICAgIGlmICh0aGlzLl9zdWIpIHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlNlc3Npb25Nb25pdG9yLl9jYWxsYmFjazogRXJyb3IgY2FsbGluZyBxdWVyeUN1cnJlbnRTaWduaW5TZXNzaW9uOyByYWlzaW5nIHNpZ25lZCBvdXQgZXZlbnRcIiwgZXJyLm1lc3NhZ2UpO1xyXG4gICAgICAgICAgICAgICAgdGhpcy5fdXNlck1hbmFnZXIuZXZlbnRzLl9yYWlzZVVzZXJTaWduZWRPdXQoKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuaW1wb3J0IHsgVXJsVXRpbGl0eSB9IGZyb20gJy4vVXJsVXRpbGl0eS5qcyc7XHJcbmltcG9ydCB7IFNpZ25pblN0YXRlIH0gZnJvbSAnLi9TaWduaW5TdGF0ZS5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgU2lnbmluUmVxdWVzdCB7XHJcbiAgICBjb25zdHJ1Y3Rvcih7XHJcbiAgICAgICAgLy8gbWFuZGF0b3J5XHJcbiAgICAgICAgdXJsLCBjbGllbnRfaWQsIHJlZGlyZWN0X3VyaSwgcmVzcG9uc2VfdHlwZSwgc2NvcGUsIGF1dGhvcml0eSxcclxuICAgICAgICAvLyBvcHRpb25hbFxyXG4gICAgICAgIGRhdGEsIHByb21wdCwgZGlzcGxheSwgbWF4X2FnZSwgdWlfbG9jYWxlcywgaWRfdG9rZW5faGludCwgbG9naW5faGludCwgYWNyX3ZhbHVlcywgcmVzb3VyY2UsIHJlc3BvbnNlX21vZGUsXHJcbiAgICAgICAgcmVxdWVzdCwgcmVxdWVzdF91cmksIGV4dHJhUXVlcnlQYXJhbXMsIHJlcXVlc3RfdHlwZSwgY2xpZW50X3NlY3JldCwgZXh0cmFUb2tlblBhcmFtcywgc2tpcFVzZXJJbmZvXHJcbiAgICB9KSB7XHJcbiAgICAgICAgaWYgKCF1cmwpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiU2lnbmluUmVxdWVzdC5jdG9yOiBObyB1cmwgcGFzc2VkXCIpO1xyXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJ1cmxcIik7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICghY2xpZW50X2lkKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlNpZ25pblJlcXVlc3QuY3RvcjogTm8gY2xpZW50X2lkIHBhc3NlZFwiKTtcclxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiY2xpZW50X2lkXCIpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoIXJlZGlyZWN0X3VyaSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJTaWduaW5SZXF1ZXN0LmN0b3I6IE5vIHJlZGlyZWN0X3VyaSBwYXNzZWRcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcInJlZGlyZWN0X3VyaVwiKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKCFyZXNwb25zZV90eXBlKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlNpZ25pblJlcXVlc3QuY3RvcjogTm8gcmVzcG9uc2VfdHlwZSBwYXNzZWRcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcInJlc3BvbnNlX3R5cGVcIik7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICghc2NvcGUpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiU2lnbmluUmVxdWVzdC5jdG9yOiBObyBzY29wZSBwYXNzZWRcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcInNjb3BlXCIpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoIWF1dGhvcml0eSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJTaWduaW5SZXF1ZXN0LmN0b3I6IE5vIGF1dGhvcml0eSBwYXNzZWRcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcImF1dGhvcml0eVwiKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGxldCBvaWRjID0gU2lnbmluUmVxdWVzdC5pc09pZGMocmVzcG9uc2VfdHlwZSk7XHJcbiAgICAgICAgbGV0IGNvZGUgPSBTaWduaW5SZXF1ZXN0LmlzQ29kZShyZXNwb25zZV90eXBlKTtcclxuXHJcbiAgICAgICAgaWYgKCFyZXNwb25zZV9tb2RlKSB7XHJcbiAgICAgICAgICAgIHJlc3BvbnNlX21vZGUgPSBTaWduaW5SZXF1ZXN0LmlzQ29kZShyZXNwb25zZV90eXBlKSA/IFwicXVlcnlcIiA6IG51bGw7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB0aGlzLnN0YXRlID0gbmV3IFNpZ25pblN0YXRlKHsgbm9uY2U6IG9pZGMsIFxyXG4gICAgICAgICAgICBkYXRhLCBjbGllbnRfaWQsIGF1dGhvcml0eSwgcmVkaXJlY3RfdXJpLCBcclxuICAgICAgICAgICAgY29kZV92ZXJpZmllcjogY29kZSwgXHJcbiAgICAgICAgICAgIHJlcXVlc3RfdHlwZSwgcmVzcG9uc2VfbW9kZSxcclxuICAgICAgICAgICAgY2xpZW50X3NlY3JldCwgc2NvcGUsIGV4dHJhVG9rZW5QYXJhbXMsIHNraXBVc2VySW5mbyB9KTtcclxuXHJcbiAgICAgICAgdXJsID0gVXJsVXRpbGl0eS5hZGRRdWVyeVBhcmFtKHVybCwgXCJjbGllbnRfaWRcIiwgY2xpZW50X2lkKTtcclxuICAgICAgICB1cmwgPSBVcmxVdGlsaXR5LmFkZFF1ZXJ5UGFyYW0odXJsLCBcInJlZGlyZWN0X3VyaVwiLCByZWRpcmVjdF91cmkpO1xyXG4gICAgICAgIHVybCA9IFVybFV0aWxpdHkuYWRkUXVlcnlQYXJhbSh1cmwsIFwicmVzcG9uc2VfdHlwZVwiLCByZXNwb25zZV90eXBlKTtcclxuICAgICAgICB1cmwgPSBVcmxVdGlsaXR5LmFkZFF1ZXJ5UGFyYW0odXJsLCBcInNjb3BlXCIsIHNjb3BlKTtcclxuXHJcbiAgICAgICAgdXJsID0gVXJsVXRpbGl0eS5hZGRRdWVyeVBhcmFtKHVybCwgXCJzdGF0ZVwiLCB0aGlzLnN0YXRlLmlkKTtcclxuICAgICAgICBpZiAob2lkYykge1xyXG4gICAgICAgICAgICB1cmwgPSBVcmxVdGlsaXR5LmFkZFF1ZXJ5UGFyYW0odXJsLCBcIm5vbmNlXCIsIHRoaXMuc3RhdGUubm9uY2UpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoY29kZSkge1xyXG4gICAgICAgICAgICB1cmwgPSBVcmxVdGlsaXR5LmFkZFF1ZXJ5UGFyYW0odXJsLCBcImNvZGVfY2hhbGxlbmdlXCIsIHRoaXMuc3RhdGUuY29kZV9jaGFsbGVuZ2UpO1xyXG4gICAgICAgICAgICB1cmwgPSBVcmxVdGlsaXR5LmFkZFF1ZXJ5UGFyYW0odXJsLCBcImNvZGVfY2hhbGxlbmdlX21ldGhvZFwiLCBcIlMyNTZcIik7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB2YXIgb3B0aW9uYWwgPSB7IHByb21wdCwgZGlzcGxheSwgbWF4X2FnZSwgdWlfbG9jYWxlcywgaWRfdG9rZW5faGludCwgbG9naW5faGludCwgYWNyX3ZhbHVlcywgcmVzb3VyY2UsIHJlcXVlc3QsIHJlcXVlc3RfdXJpLCByZXNwb25zZV9tb2RlIH07XHJcbiAgICAgICAgZm9yKGxldCBrZXkgaW4gb3B0aW9uYWwpe1xyXG4gICAgICAgICAgICBpZiAob3B0aW9uYWxba2V5XSkge1xyXG4gICAgICAgICAgICAgICAgdXJsID0gVXJsVXRpbGl0eS5hZGRRdWVyeVBhcmFtKHVybCwga2V5LCBvcHRpb25hbFtrZXldKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZm9yKGxldCBrZXkgaW4gZXh0cmFRdWVyeVBhcmFtcyl7XHJcbiAgICAgICAgICAgIHVybCA9IFVybFV0aWxpdHkuYWRkUXVlcnlQYXJhbSh1cmwsIGtleSwgZXh0cmFRdWVyeVBhcmFtc1trZXldKVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy51cmwgPSB1cmw7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhdGljIGlzT2lkYyhyZXNwb25zZV90eXBlKSB7XHJcbiAgICAgICAgdmFyIHJlc3VsdCA9IHJlc3BvbnNlX3R5cGUuc3BsaXQoL1xccysvZykuZmlsdGVyKGZ1bmN0aW9uKGl0ZW0pIHtcclxuICAgICAgICAgICAgcmV0dXJuIGl0ZW0gPT09IFwiaWRfdG9rZW5cIjtcclxuICAgICAgICB9KTtcclxuICAgICAgICByZXR1cm4gISEocmVzdWx0WzBdKTtcclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWMgaXNPQXV0aChyZXNwb25zZV90eXBlKSB7XHJcbiAgICAgICAgdmFyIHJlc3VsdCA9IHJlc3BvbnNlX3R5cGUuc3BsaXQoL1xccysvZykuZmlsdGVyKGZ1bmN0aW9uKGl0ZW0pIHtcclxuICAgICAgICAgICAgcmV0dXJuIGl0ZW0gPT09IFwidG9rZW5cIjtcclxuICAgICAgICB9KTtcclxuICAgICAgICByZXR1cm4gISEocmVzdWx0WzBdKTtcclxuICAgIH1cclxuICAgIFxyXG4gICAgc3RhdGljIGlzQ29kZShyZXNwb25zZV90eXBlKSB7XHJcbiAgICAgICAgdmFyIHJlc3VsdCA9IHJlc3BvbnNlX3R5cGUuc3BsaXQoL1xccysvZykuZmlsdGVyKGZ1bmN0aW9uKGl0ZW0pIHtcclxuICAgICAgICAgICAgcmV0dXJuIGl0ZW0gPT09IFwiY29kZVwiO1xyXG4gICAgICAgIH0pO1xyXG4gICAgICAgIHJldHVybiAhIShyZXN1bHRbMF0pO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IFVybFV0aWxpdHkgfSBmcm9tICcuL1VybFV0aWxpdHkuanMnO1xyXG5cclxuY29uc3QgT2lkY1Njb3BlID0gXCJvcGVuaWRcIjtcclxuXHJcbmV4cG9ydCBjbGFzcyBTaWduaW5SZXNwb25zZSB7XHJcbiAgICBjb25zdHJ1Y3Rvcih1cmwsIGRlbGltaXRlciA9IFwiI1wiKSB7XHJcblxyXG4gICAgICAgIHZhciB2YWx1ZXMgPSBVcmxVdGlsaXR5LnBhcnNlVXJsRnJhZ21lbnQodXJsLCBkZWxpbWl0ZXIpO1xyXG5cclxuICAgICAgICB0aGlzLmVycm9yID0gdmFsdWVzLmVycm9yO1xyXG4gICAgICAgIHRoaXMuZXJyb3JfZGVzY3JpcHRpb24gPSB2YWx1ZXMuZXJyb3JfZGVzY3JpcHRpb247XHJcbiAgICAgICAgdGhpcy5lcnJvcl91cmkgPSB2YWx1ZXMuZXJyb3JfdXJpO1xyXG5cclxuICAgICAgICB0aGlzLmNvZGUgPSB2YWx1ZXMuY29kZTtcclxuICAgICAgICB0aGlzLnN0YXRlID0gdmFsdWVzLnN0YXRlO1xyXG4gICAgICAgIHRoaXMuaWRfdG9rZW4gPSB2YWx1ZXMuaWRfdG9rZW47XHJcbiAgICAgICAgdGhpcy5zZXNzaW9uX3N0YXRlID0gdmFsdWVzLnNlc3Npb25fc3RhdGU7XHJcbiAgICAgICAgdGhpcy5hY2Nlc3NfdG9rZW4gPSB2YWx1ZXMuYWNjZXNzX3Rva2VuO1xyXG4gICAgICAgIHRoaXMudG9rZW5fdHlwZSA9IHZhbHVlcy50b2tlbl90eXBlO1xyXG4gICAgICAgIHRoaXMuc2NvcGUgPSB2YWx1ZXMuc2NvcGU7XHJcbiAgICAgICAgdGhpcy5wcm9maWxlID0gdW5kZWZpbmVkOyAvLyB3aWxsIGJlIHNldCBmcm9tIFJlc3BvbnNlVmFsaWRhdG9yXHJcblxyXG4gICAgICAgIHRoaXMuZXhwaXJlc19pbiA9IHZhbHVlcy5leHBpcmVzX2luO1xyXG4gICAgfVxyXG5cclxuICAgIGdldCBleHBpcmVzX2luKCkge1xyXG4gICAgICAgIGlmICh0aGlzLmV4cGlyZXNfYXQpIHtcclxuICAgICAgICAgICAgbGV0IG5vdyA9IHBhcnNlSW50KERhdGUubm93KCkgLyAxMDAwKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZXhwaXJlc19hdCAtIG5vdztcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICAgIH1cclxuICAgIHNldCBleHBpcmVzX2luKHZhbHVlKXtcclxuICAgICAgICBsZXQgZXhwaXJlc19pbiA9IHBhcnNlSW50KHZhbHVlKTtcclxuICAgICAgICBpZiAodHlwZW9mIGV4cGlyZXNfaW4gPT09ICdudW1iZXInICYmIGV4cGlyZXNfaW4gPiAwKSB7XHJcbiAgICAgICAgICAgIGxldCBub3cgPSBwYXJzZUludChEYXRlLm5vdygpIC8gMTAwMCk7XHJcbiAgICAgICAgICAgIHRoaXMuZXhwaXJlc19hdCA9IG5vdyArIGV4cGlyZXNfaW47XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGdldCBleHBpcmVkKCkge1xyXG4gICAgICAgIGxldCBleHBpcmVzX2luID0gdGhpcy5leHBpcmVzX2luO1xyXG4gICAgICAgIGlmIChleHBpcmVzX2luICE9PSB1bmRlZmluZWQpIHtcclxuICAgICAgICAgICAgcmV0dXJuIGV4cGlyZXNfaW4gPD0gMDtcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgc2NvcGVzKCkge1xyXG4gICAgICAgIHJldHVybiAodGhpcy5zY29wZSB8fCBcIlwiKS5zcGxpdChcIiBcIik7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IGlzT3BlbklkQ29ubmVjdCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5zY29wZXMuaW5kZXhPZihPaWRjU2NvcGUpID49IDAgfHwgISF0aGlzLmlkX3Rva2VuO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuaW1wb3J0IHsgU3RhdGUgfSBmcm9tICcuL1N0YXRlLmpzJztcclxuaW1wb3J0IHsgSm9zZVV0aWwgfSBmcm9tICcuL0pvc2VVdGlsLmpzJztcclxuaW1wb3J0IHJhbmRvbSBmcm9tICcuL3JhbmRvbS5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgU2lnbmluU3RhdGUgZXh0ZW5kcyBTdGF0ZSB7XHJcbiAgICBjb25zdHJ1Y3Rvcih7bm9uY2UsIGF1dGhvcml0eSwgY2xpZW50X2lkLCByZWRpcmVjdF91cmksIGNvZGVfdmVyaWZpZXIsIHJlc3BvbnNlX21vZGUsIGNsaWVudF9zZWNyZXQsIHNjb3BlLCBleHRyYVRva2VuUGFyYW1zLCBza2lwVXNlckluZm99ID0ge30pIHtcclxuICAgICAgICBzdXBlcihhcmd1bWVudHNbMF0pO1xyXG5cclxuICAgICAgICBpZiAobm9uY2UgPT09IHRydWUpIHtcclxuICAgICAgICAgICAgdGhpcy5fbm9uY2UgPSByYW5kb20oKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSBpZiAobm9uY2UpIHtcclxuICAgICAgICAgICAgdGhpcy5fbm9uY2UgPSBub25jZTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmIChjb2RlX3ZlcmlmaWVyID09PSB0cnVlKSB7XHJcbiAgICAgICAgICAgIC8vIHJhbmRvbSgpIHByb2R1Y2VzIDMyIGxlbmd0aFxyXG4gICAgICAgICAgICB0aGlzLl9jb2RlX3ZlcmlmaWVyID0gcmFuZG9tKCkgKyByYW5kb20oKSArIHJhbmRvbSgpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIGlmIChjb2RlX3ZlcmlmaWVyKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NvZGVfdmVyaWZpZXIgPSBjb2RlX3ZlcmlmaWVyO1xyXG4gICAgICAgIH1cclxuICAgICAgICBcclxuICAgICAgICBpZiAodGhpcy5jb2RlX3ZlcmlmaWVyKSB7XHJcbiAgICAgICAgICAgIGxldCBoYXNoID0gSm9zZVV0aWwuaGFzaFN0cmluZyh0aGlzLmNvZGVfdmVyaWZpZXIsIFwiU0hBMjU2XCIpO1xyXG4gICAgICAgICAgICB0aGlzLl9jb2RlX2NoYWxsZW5nZSA9IEpvc2VVdGlsLmhleFRvQmFzZTY0VXJsKGhhc2gpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fcmVkaXJlY3RfdXJpID0gcmVkaXJlY3RfdXJpO1xyXG4gICAgICAgIHRoaXMuX2F1dGhvcml0eSA9IGF1dGhvcml0eTtcclxuICAgICAgICB0aGlzLl9jbGllbnRfaWQgPSBjbGllbnRfaWQ7XHJcbiAgICAgICAgdGhpcy5fcmVzcG9uc2VfbW9kZSA9IHJlc3BvbnNlX21vZGU7XHJcbiAgICAgICAgdGhpcy5fY2xpZW50X3NlY3JldCA9IGNsaWVudF9zZWNyZXQ7XHJcbiAgICAgICAgdGhpcy5fc2NvcGUgPSBzY29wZTtcclxuICAgICAgICB0aGlzLl9leHRyYVRva2VuUGFyYW1zID0gZXh0cmFUb2tlblBhcmFtcztcclxuICAgICAgICB0aGlzLl9za2lwVXNlckluZm8gPSBza2lwVXNlckluZm87XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IG5vbmNlKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9ub25jZTtcclxuICAgIH1cclxuICAgIGdldCBhdXRob3JpdHkoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2F1dGhvcml0eTtcclxuICAgIH1cclxuICAgIGdldCBjbGllbnRfaWQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NsaWVudF9pZDtcclxuICAgIH1cclxuICAgIGdldCByZWRpcmVjdF91cmkoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3JlZGlyZWN0X3VyaTtcclxuICAgIH1cclxuICAgIGdldCBjb2RlX3ZlcmlmaWVyKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9jb2RlX3ZlcmlmaWVyO1xyXG4gICAgfVxyXG4gICAgZ2V0IGNvZGVfY2hhbGxlbmdlKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9jb2RlX2NoYWxsZW5nZTtcclxuICAgIH1cclxuICAgIGdldCByZXNwb25zZV9tb2RlKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9yZXNwb25zZV9tb2RlO1xyXG4gICAgfVxyXG4gICAgZ2V0IGNsaWVudF9zZWNyZXQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2NsaWVudF9zZWNyZXQ7XHJcbiAgICB9XHJcbiAgICBnZXQgc2NvcGUoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Njb3BlO1xyXG4gICAgfVxyXG4gICAgZ2V0IGV4dHJhVG9rZW5QYXJhbXMoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2V4dHJhVG9rZW5QYXJhbXM7XHJcbiAgICB9XHJcbiAgICBnZXQgc2tpcFVzZXJJbmZvKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9za2lwVXNlckluZm87XHJcbiAgICB9XHJcbiAgICBcclxuICAgIHRvU3RvcmFnZVN0cmluZygpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJTaWduaW5TdGF0ZS50b1N0b3JhZ2VTdHJpbmdcIik7XHJcbiAgICAgICAgcmV0dXJuIEpTT04uc3RyaW5naWZ5KHtcclxuICAgICAgICAgICAgaWQ6IHRoaXMuaWQsXHJcbiAgICAgICAgICAgIGRhdGE6IHRoaXMuZGF0YSxcclxuICAgICAgICAgICAgY3JlYXRlZDogdGhpcy5jcmVhdGVkLFxyXG4gICAgICAgICAgICByZXF1ZXN0X3R5cGU6IHRoaXMucmVxdWVzdF90eXBlLFxyXG4gICAgICAgICAgICBub25jZTogdGhpcy5ub25jZSxcclxuICAgICAgICAgICAgY29kZV92ZXJpZmllcjogdGhpcy5jb2RlX3ZlcmlmaWVyLFxyXG4gICAgICAgICAgICByZWRpcmVjdF91cmk6IHRoaXMucmVkaXJlY3RfdXJpLFxyXG4gICAgICAgICAgICBhdXRob3JpdHk6IHRoaXMuYXV0aG9yaXR5LFxyXG4gICAgICAgICAgICBjbGllbnRfaWQ6IHRoaXMuY2xpZW50X2lkLFxyXG4gICAgICAgICAgICByZXNwb25zZV9tb2RlOiB0aGlzLnJlc3BvbnNlX21vZGUsXHJcbiAgICAgICAgICAgIGNsaWVudF9zZWNyZXQ6IHRoaXMuY2xpZW50X3NlY3JldCxcclxuICAgICAgICAgICAgc2NvcGU6IHRoaXMuc2NvcGUsXHJcbiAgICAgICAgICAgIGV4dHJhVG9rZW5QYXJhbXMgOiB0aGlzLmV4dHJhVG9rZW5QYXJhbXMsXHJcbiAgICAgICAgICAgIHNraXBVc2VySW5mbzogdGhpcy5za2lwVXNlckluZm9cclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWMgZnJvbVN0b3JhZ2VTdHJpbmcoc3RvcmFnZVN0cmluZykge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlNpZ25pblN0YXRlLmZyb21TdG9yYWdlU3RyaW5nXCIpO1xyXG4gICAgICAgIHZhciBkYXRhID0gSlNPTi5wYXJzZShzdG9yYWdlU3RyaW5nKTtcclxuICAgICAgICByZXR1cm4gbmV3IFNpZ25pblN0YXRlKGRhdGEpO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuaW1wb3J0IHsgVXJsVXRpbGl0eSB9IGZyb20gJy4vVXJsVXRpbGl0eS5qcyc7XHJcbmltcG9ydCB7IFN0YXRlIH0gZnJvbSAnLi9TdGF0ZS5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgU2lnbm91dFJlcXVlc3Qge1xyXG4gICAgY29uc3RydWN0b3Ioe3VybCwgaWRfdG9rZW5faGludCwgcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpLCBkYXRhLCBleHRyYVF1ZXJ5UGFyYW1zLCByZXF1ZXN0X3R5cGV9KSB7XHJcbiAgICAgICAgaWYgKCF1cmwpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiU2lnbm91dFJlcXVlc3QuY3RvcjogTm8gdXJsIHBhc3NlZFwiKTtcclxuICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwidXJsXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKGlkX3Rva2VuX2hpbnQpIHtcclxuICAgICAgICAgICAgdXJsID0gVXJsVXRpbGl0eS5hZGRRdWVyeVBhcmFtKHVybCwgXCJpZF90b2tlbl9oaW50XCIsIGlkX3Rva2VuX2hpbnQpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgaWYgKHBvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaSkge1xyXG4gICAgICAgICAgICB1cmwgPSBVcmxVdGlsaXR5LmFkZFF1ZXJ5UGFyYW0odXJsLCBcInBvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaVwiLCBwb3N0X2xvZ291dF9yZWRpcmVjdF91cmkpO1xyXG5cclxuICAgICAgICAgICAgaWYgKGRhdGEpIHtcclxuICAgICAgICAgICAgICAgIHRoaXMuc3RhdGUgPSBuZXcgU3RhdGUoeyBkYXRhLCByZXF1ZXN0X3R5cGUgfSk7XHJcblxyXG4gICAgICAgICAgICAgICAgdXJsID0gVXJsVXRpbGl0eS5hZGRRdWVyeVBhcmFtKHVybCwgXCJzdGF0ZVwiLCB0aGlzLnN0YXRlLmlkKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgZm9yKGxldCBrZXkgaW4gZXh0cmFRdWVyeVBhcmFtcyl7XHJcbiAgICAgICAgICAgIHVybCA9IFVybFV0aWxpdHkuYWRkUXVlcnlQYXJhbSh1cmwsIGtleSwgZXh0cmFRdWVyeVBhcmFtc1trZXldKVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy51cmwgPSB1cmw7XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgVXJsVXRpbGl0eSB9IGZyb20gJy4vVXJsVXRpbGl0eS5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgU2lnbm91dFJlc3BvbnNlIHtcclxuICAgIGNvbnN0cnVjdG9yKHVybCkge1xyXG5cclxuICAgICAgICB2YXIgdmFsdWVzID0gVXJsVXRpbGl0eS5wYXJzZVVybEZyYWdtZW50KHVybCwgXCI/XCIpO1xyXG5cclxuICAgICAgICB0aGlzLmVycm9yID0gdmFsdWVzLmVycm9yO1xyXG4gICAgICAgIHRoaXMuZXJyb3JfZGVzY3JpcHRpb24gPSB2YWx1ZXMuZXJyb3JfZGVzY3JpcHRpb247XHJcbiAgICAgICAgdGhpcy5lcnJvcl91cmkgPSB2YWx1ZXMuZXJyb3JfdXJpO1xyXG5cclxuICAgICAgICB0aGlzLnN0YXRlID0gdmFsdWVzLnN0YXRlO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBTaWxlbnRSZW5ld1NlcnZpY2Uge1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKHVzZXJNYW5hZ2VyKSB7XHJcbiAgICAgICAgdGhpcy5fdXNlck1hbmFnZXIgPSB1c2VyTWFuYWdlcjtcclxuICAgIH1cclxuXHJcbiAgICBzdGFydCgpIHtcclxuICAgICAgICBpZiAoIXRoaXMuX2NhbGxiYWNrKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NhbGxiYWNrID0gdGhpcy5fdG9rZW5FeHBpcmluZy5iaW5kKHRoaXMpO1xyXG4gICAgICAgICAgICB0aGlzLl91c2VyTWFuYWdlci5ldmVudHMuYWRkQWNjZXNzVG9rZW5FeHBpcmluZyh0aGlzLl9jYWxsYmFjayk7XHJcblxyXG4gICAgICAgICAgICAvLyB0aGlzIHdpbGwgdHJpZ2dlciBsb2FkaW5nIG9mIHRoZSB1c2VyIHNvIHRoZSBleHBpcmluZyBldmVudHMgY2FuIGJlIGluaXRpYWxpemVkXHJcbiAgICAgICAgICAgIHRoaXMuX3VzZXJNYW5hZ2VyLmdldFVzZXIoKS50aGVuKHVzZXI9PntcclxuICAgICAgICAgICAgICAgIC8vIGRlbGliZXJhdGUgbm9wXHJcbiAgICAgICAgICAgIH0pLmNhdGNoKGVycj0+e1xyXG4gICAgICAgICAgICAgICAgLy8gY2F0Y2ggdG8gc3VwcHJlc3MgZXJyb3JzIHNpbmNlIHdlJ3JlIGluIGEgY3RvclxyXG4gICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiU2lsZW50UmVuZXdTZXJ2aWNlLnN0YXJ0OiBFcnJvciBmcm9tIGdldFVzZXI6XCIsIGVyci5tZXNzYWdlKTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIHN0b3AoKSB7XHJcbiAgICAgICAgaWYgKHRoaXMuX2NhbGxiYWNrKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX3VzZXJNYW5hZ2VyLmV2ZW50cy5yZW1vdmVBY2Nlc3NUb2tlbkV4cGlyaW5nKHRoaXMuX2NhbGxiYWNrKTtcclxuICAgICAgICAgICAgZGVsZXRlIHRoaXMuX2NhbGxiYWNrO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBfdG9rZW5FeHBpcmluZygpIHtcclxuICAgICAgICB0aGlzLl91c2VyTWFuYWdlci5zaWduaW5TaWxlbnQoKS50aGVuKHVzZXIgPT4ge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJTaWxlbnRSZW5ld1NlcnZpY2UuX3Rva2VuRXhwaXJpbmc6IFNpbGVudCB0b2tlbiByZW5ld2FsIHN1Y2Nlc3NmdWxcIik7XHJcbiAgICAgICAgfSwgZXJyID0+IHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiU2lsZW50UmVuZXdTZXJ2aWNlLl90b2tlbkV4cGlyaW5nOiBFcnJvciBmcm9tIHNpZ25pblNpbGVudDpcIiwgZXJyLm1lc3NhZ2UpO1xyXG4gICAgICAgICAgICB0aGlzLl91c2VyTWFuYWdlci5ldmVudHMuX3JhaXNlU2lsZW50UmVuZXdFcnJvcihlcnIpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuaW1wb3J0IHJhbmRvbSBmcm9tICcuL3JhbmRvbS5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgU3RhdGUge1xyXG4gICAgY29uc3RydWN0b3Ioe2lkLCBkYXRhLCBjcmVhdGVkLCByZXF1ZXN0X3R5cGV9ID0ge30pIHtcclxuICAgICAgICB0aGlzLl9pZCA9IGlkIHx8IHJhbmRvbSgpO1xyXG4gICAgICAgIHRoaXMuX2RhdGEgPSBkYXRhO1xyXG5cclxuICAgICAgICBpZiAodHlwZW9mIGNyZWF0ZWQgPT09ICdudW1iZXInICYmIGNyZWF0ZWQgPiAwKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX2NyZWF0ZWQgPSBjcmVhdGVkO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgdGhpcy5fY3JlYXRlZCA9IHBhcnNlSW50KERhdGUubm93KCkgLyAxMDAwKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgdGhpcy5fcmVxdWVzdF90eXBlID0gIHJlcXVlc3RfdHlwZTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgaWQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2lkO1xyXG4gICAgfVxyXG4gICAgZ2V0IGRhdGEoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2RhdGE7XHJcbiAgICB9XHJcbiAgICBnZXQgY3JlYXRlZCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fY3JlYXRlZDtcclxuICAgIH1cclxuICAgIGdldCByZXF1ZXN0X3R5cGUoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3JlcXVlc3RfdHlwZTtcclxuICAgIH1cclxuXHJcbiAgICB0b1N0b3JhZ2VTdHJpbmcoKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiU3RhdGUudG9TdG9yYWdlU3RyaW5nXCIpO1xyXG4gICAgICAgIHJldHVybiBKU09OLnN0cmluZ2lmeSh7XHJcbiAgICAgICAgICAgIGlkOiB0aGlzLmlkLFxyXG4gICAgICAgICAgICBkYXRhOiB0aGlzLmRhdGEsXHJcbiAgICAgICAgICAgIGNyZWF0ZWQ6IHRoaXMuY3JlYXRlZCxcclxuICAgICAgICAgICAgcmVxdWVzdF90eXBlOiB0aGlzLnJlcXVlc3RfdHlwZVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHN0YXRpYyBmcm9tU3RvcmFnZVN0cmluZyhzdG9yYWdlU3RyaW5nKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiU3RhdGUuZnJvbVN0b3JhZ2VTdHJpbmdcIik7XHJcbiAgICAgICAgcmV0dXJuIG5ldyBTdGF0ZShKU09OLnBhcnNlKHN0b3JhZ2VTdHJpbmcpKTtcclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWMgY2xlYXJTdGFsZVN0YXRlKHN0b3JhZ2UsIGFnZSkge1xyXG5cclxuICAgICAgICB2YXIgY3V0b2ZmID0gRGF0ZS5ub3coKSAvIDEwMDAgLSBhZ2U7XHJcblxyXG4gICAgICAgIHJldHVybiBzdG9yYWdlLmdldEFsbEtleXMoKS50aGVuKGtleXMgPT4ge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJTdGF0ZS5jbGVhclN0YWxlU3RhdGU6IGdvdCBrZXlzXCIsIGtleXMpO1xyXG5cclxuICAgICAgICAgICAgdmFyIHByb21pc2VzID0gW107XHJcbiAgICAgICAgICAgIGZvciAobGV0IGkgPSAwOyBpIDwga2V5cy5sZW5ndGg7IGkrKykge1xyXG4gICAgICAgICAgICAgICAgbGV0IGtleSA9IGtleXNbaV07XHJcbiAgICAgICAgICAgICAgICB2YXIgcCA9IHN0b3JhZ2UuZ2V0KGtleSkudGhlbihpdGVtID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBsZXQgcmVtb3ZlID0gZmFsc2U7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGlmIChpdGVtKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB2YXIgc3RhdGUgPSBTdGF0ZS5mcm9tU3RvcmFnZVN0cmluZyhpdGVtKVxyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlN0YXRlLmNsZWFyU3RhbGVTdGF0ZTogZ290IGl0ZW0gZnJvbSBrZXk6IFwiLCBrZXksIHN0YXRlLmNyZWF0ZWQpO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGlmIChzdGF0ZS5jcmVhdGVkIDw9IGN1dG9mZikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZSA9IHRydWU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgY2F0Y2ggKGUpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlN0YXRlLmNsZWFyU3RhbGVTdGF0ZTogRXJyb3IgcGFyc2luZyBzdGF0ZSBmb3Iga2V5XCIsIGtleSwgZS5tZXNzYWdlKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJlbW92ZSA9IHRydWU7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlN0YXRlLmNsZWFyU3RhbGVTdGF0ZTogbm8gaXRlbSBpbiBzdG9yYWdlIGZvciBrZXk6IFwiLCBrZXkpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZW1vdmUgPSB0cnVlO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJlbW92ZSkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJTdGF0ZS5jbGVhclN0YWxlU3RhdGU6IHJlbW92ZWQgaXRlbSBmb3Iga2V5OiBcIiwga2V5KTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHN0b3JhZ2UucmVtb3ZlKGtleSk7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgfSk7XHJcblxyXG4gICAgICAgICAgICAgICAgcHJvbWlzZXMucHVzaChwKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiU3RhdGUuY2xlYXJTdGFsZVN0YXRlOiB3YWl0aW5nIG9uIHByb21pc2UgY291bnQ6XCIsIHByb21pc2VzLmxlbmd0aCk7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLmFsbChwcm9taXNlcyk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5pbXBvcnQgeyBHbG9iYWwgfSBmcm9tICcuL0dsb2JhbC5qcyc7XHJcbmltcG9ydCB7IEV2ZW50IH0gZnJvbSAnLi9FdmVudC5qcyc7XHJcblxyXG5jb25zdCBUaW1lckR1cmF0aW9uID0gNTsgLy8gc2Vjb25kc1xyXG5cclxuZXhwb3J0IGNsYXNzIFRpbWVyIGV4dGVuZHMgRXZlbnQge1xyXG5cclxuICAgIGNvbnN0cnVjdG9yKG5hbWUsIHRpbWVyID0gR2xvYmFsLnRpbWVyLCBub3dGdW5jID0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgc3VwZXIobmFtZSk7XHJcbiAgICAgICAgdGhpcy5fdGltZXIgPSB0aW1lcjtcclxuXHJcbiAgICAgICAgaWYgKG5vd0Z1bmMpIHtcclxuICAgICAgICAgICAgdGhpcy5fbm93RnVuYyA9IG5vd0Z1bmM7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICB0aGlzLl9ub3dGdW5jID0gKCkgPT4gRGF0ZS5ub3coKSAvIDEwMDA7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIGdldCBub3coKSB7XHJcbiAgICAgICAgcmV0dXJuIHBhcnNlSW50KHRoaXMuX25vd0Z1bmMoKSk7XHJcbiAgICB9XHJcblxyXG4gICAgaW5pdChkdXJhdGlvbikge1xyXG4gICAgICAgIGlmIChkdXJhdGlvbiA8PSAwKSB7XHJcbiAgICAgICAgICAgIGR1cmF0aW9uID0gMTtcclxuICAgICAgICB9XHJcbiAgICAgICAgZHVyYXRpb24gPSBwYXJzZUludChkdXJhdGlvbik7XHJcblxyXG4gICAgICAgIHZhciBleHBpcmF0aW9uID0gdGhpcy5ub3cgKyBkdXJhdGlvbjtcclxuICAgICAgICBpZiAodGhpcy5leHBpcmF0aW9uID09PSBleHBpcmF0aW9uICYmIHRoaXMuX3RpbWVySGFuZGxlKSB7XHJcbiAgICAgICAgICAgIC8vIG5vIG5lZWQgdG8gcmVpbml0aWFsaXplIHRvIHNhbWUgZXhwaXJhdGlvbiwgc28gYmFpbCBvdXRcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiVGltZXIuaW5pdCB0aW1lciBcIiArIHRoaXMuX25hbWUgKyBcIiBza2lwcGluZyBpbml0aWFsaXphdGlvbiBzaW5jZSBhbHJlYWR5IGluaXRpYWxpemVkIGZvciBleHBpcmF0aW9uOlwiLCB0aGlzLmV4cGlyYXRpb24pO1xyXG4gICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB0aGlzLmNhbmNlbCgpO1xyXG5cclxuICAgICAgICBMb2cuZGVidWcoXCJUaW1lci5pbml0IHRpbWVyIFwiICsgdGhpcy5fbmFtZSArIFwiIGZvciBkdXJhdGlvbjpcIiwgZHVyYXRpb24pO1xyXG4gICAgICAgIHRoaXMuX2V4cGlyYXRpb24gPSBleHBpcmF0aW9uO1xyXG5cclxuICAgICAgICAvLyB3ZSdyZSB1c2luZyBhIGZhaXJseSBzaG9ydCB0aW1lciBhbmQgdGhlbiBjaGVja2luZyB0aGUgZXhwaXJhdGlvbiBpbiB0aGVcclxuICAgICAgICAvLyBjYWxsYmFjayB0byBoYW5kbGUgc2NlbmFyaW9zIHdoZXJlIHRoZSBicm93c2VyIGRldmljZSBzbGVlcHMsIGFuZCB0aGVuXHJcbiAgICAgICAgLy8gdGhlIHRpbWVycyBlbmQgdXAgZ2V0dGluZyBkZWxheWVkLlxyXG4gICAgICAgIHZhciB0aW1lckR1cmF0aW9uID0gVGltZXJEdXJhdGlvbjtcclxuICAgICAgICBpZiAoZHVyYXRpb24gPCB0aW1lckR1cmF0aW9uKSB7XHJcbiAgICAgICAgICAgIHRpbWVyRHVyYXRpb24gPSBkdXJhdGlvbjtcclxuICAgICAgICB9XHJcbiAgICAgICAgdGhpcy5fdGltZXJIYW5kbGUgPSB0aGlzLl90aW1lci5zZXRJbnRlcnZhbCh0aGlzLl9jYWxsYmFjay5iaW5kKHRoaXMpLCB0aW1lckR1cmF0aW9uICogMTAwMCk7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIGdldCBleHBpcmF0aW9uKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9leHBpcmF0aW9uO1xyXG4gICAgfVxyXG5cclxuICAgIGNhbmNlbCgpIHtcclxuICAgICAgICBpZiAodGhpcy5fdGltZXJIYW5kbGUpIHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiVGltZXIuY2FuY2VsOiBcIiwgdGhpcy5fbmFtZSk7XHJcbiAgICAgICAgICAgIHRoaXMuX3RpbWVyLmNsZWFySW50ZXJ2YWwodGhpcy5fdGltZXJIYW5kbGUpO1xyXG4gICAgICAgICAgICB0aGlzLl90aW1lckhhbmRsZSA9IG51bGw7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG5cclxuICAgIF9jYWxsYmFjaygpIHtcclxuICAgICAgICB2YXIgZGlmZiA9IHRoaXMuX2V4cGlyYXRpb24gLSB0aGlzLm5vdztcclxuICAgICAgICBMb2cuZGVidWcoXCJUaW1lci5jYWxsYmFjazsgXCIgKyB0aGlzLl9uYW1lICsgXCIgdGltZXIgZXhwaXJlcyBpbjpcIiwgZGlmZik7XHJcblxyXG4gICAgICAgIGlmICh0aGlzLl9leHBpcmF0aW9uIDw9IHRoaXMubm93KSB7XHJcbiAgICAgICAgICAgIHRoaXMuY2FuY2VsKCk7XHJcbiAgICAgICAgICAgIHN1cGVyLnJhaXNlKCk7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IEpzb25TZXJ2aWNlIH0gZnJvbSAnLi9Kc29uU2VydmljZS5qcyc7XHJcbmltcG9ydCB7IE1ldGFkYXRhU2VydmljZSB9IGZyb20gJy4vTWV0YWRhdGFTZXJ2aWNlLmpzJztcclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5cclxuZXhwb3J0IGNsYXNzIFRva2VuQ2xpZW50IHtcclxuICAgIGNvbnN0cnVjdG9yKHNldHRpbmdzLCBKc29uU2VydmljZUN0b3IgPSBKc29uU2VydmljZSwgTWV0YWRhdGFTZXJ2aWNlQ3RvciA9IE1ldGFkYXRhU2VydmljZSkge1xyXG4gICAgICAgIGlmICghc2V0dGluZ3MpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiVG9rZW5DbGllbnQuY3RvcjogTm8gc2V0dGluZ3MgcGFzc2VkXCIpO1xyXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJzZXR0aW5nc1wiKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMuX3NldHRpbmdzID0gc2V0dGluZ3M7XHJcbiAgICAgICAgdGhpcy5fanNvblNlcnZpY2UgPSBuZXcgSnNvblNlcnZpY2VDdG9yKCk7XHJcbiAgICAgICAgdGhpcy5fbWV0YWRhdGFTZXJ2aWNlID0gbmV3IE1ldGFkYXRhU2VydmljZUN0b3IodGhpcy5fc2V0dGluZ3MpO1xyXG4gICAgfVxyXG5cclxuICAgIGV4Y2hhbmdlQ29kZShhcmdzID0ge30pIHtcclxuICAgICAgICBhcmdzID0gT2JqZWN0LmFzc2lnbih7fSwgYXJncyk7XHJcblxyXG4gICAgICAgIGFyZ3MuZ3JhbnRfdHlwZSA9IGFyZ3MuZ3JhbnRfdHlwZSB8fCBcImF1dGhvcml6YXRpb25fY29kZVwiO1xyXG4gICAgICAgIGFyZ3MuY2xpZW50X2lkID0gYXJncy5jbGllbnRfaWQgfHwgdGhpcy5fc2V0dGluZ3MuY2xpZW50X2lkO1xyXG4gICAgICAgIGFyZ3MucmVkaXJlY3RfdXJpID0gYXJncy5yZWRpcmVjdF91cmkgfHwgdGhpcy5fc2V0dGluZ3MucmVkaXJlY3RfdXJpO1xyXG5cclxuICAgICAgICBpZiAoIWFyZ3MuY29kZSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJUb2tlbkNsaWVudC5leGNoYW5nZUNvZGU6IE5vIGNvZGUgcGFzc2VkXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiQSBjb2RlIGlzIHJlcXVpcmVkXCIpKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKCFhcmdzLnJlZGlyZWN0X3VyaSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJUb2tlbkNsaWVudC5leGNoYW5nZUNvZGU6IE5vIHJlZGlyZWN0X3VyaSBwYXNzZWRcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJBIHJlZGlyZWN0X3VyaSBpcyByZXF1aXJlZFwiKSk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGlmICghYXJncy5jb2RlX3ZlcmlmaWVyKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlRva2VuQ2xpZW50LmV4Y2hhbmdlQ29kZTogTm8gY29kZV92ZXJpZmllciBwYXNzZWRcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJBIGNvZGVfdmVyaWZpZXIgaXMgcmVxdWlyZWRcIikpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBpZiAoIWFyZ3MuY2xpZW50X2lkKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlRva2VuQ2xpZW50LmV4Y2hhbmdlQ29kZTogTm8gY2xpZW50X2lkIHBhc3NlZFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIkEgY2xpZW50X2lkIGlzIHJlcXVpcmVkXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLl9tZXRhZGF0YVNlcnZpY2UuZ2V0VG9rZW5FbmRwb2ludChmYWxzZSkudGhlbih1cmwgPT4ge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJUb2tlbkNsaWVudC5leGNoYW5nZUNvZGU6IFJlY2VpdmVkIHRva2VuIGVuZHBvaW50XCIpO1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2pzb25TZXJ2aWNlLnBvc3RGb3JtKHVybCwgYXJncykudGhlbihyZXNwb25zZSA9PiB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJUb2tlbkNsaWVudC5leGNoYW5nZUNvZGU6IHJlc3BvbnNlIHJlY2VpdmVkXCIpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBleGNoYW5nZVJlZnJlc2hUb2tlbihhcmdzID0ge30pIHtcclxuICAgICAgICBhcmdzID0gT2JqZWN0LmFzc2lnbih7fSwgYXJncyk7XHJcblxyXG4gICAgICAgIGFyZ3MuZ3JhbnRfdHlwZSA9IGFyZ3MuZ3JhbnRfdHlwZSB8fCBcInJlZnJlc2hfdG9rZW5cIjtcclxuICAgICAgICBhcmdzLmNsaWVudF9pZCA9IGFyZ3MuY2xpZW50X2lkIHx8IHRoaXMuX3NldHRpbmdzLmNsaWVudF9pZDtcclxuICAgICAgICBhcmdzLmNsaWVudF9zZWNyZXQgPSBhcmdzLmNsaWVudF9zZWNyZXQgfHwgdGhpcy5fc2V0dGluZ3MuY2xpZW50X3NlY3JldDtcclxuXHJcbiAgICAgICAgaWYgKCFhcmdzLnJlZnJlc2hfdG9rZW4pIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiVG9rZW5DbGllbnQuZXhjaGFuZ2VSZWZyZXNoVG9rZW46IE5vIHJlZnJlc2hfdG9rZW4gcGFzc2VkXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiQSByZWZyZXNoX3Rva2VuIGlzIHJlcXVpcmVkXCIpKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgaWYgKCFhcmdzLmNsaWVudF9pZCkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJUb2tlbkNsaWVudC5leGNoYW5nZVJlZnJlc2hUb2tlbjogTm8gY2xpZW50X2lkIHBhc3NlZFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIkEgY2xpZW50X2lkIGlzIHJlcXVpcmVkXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLl9tZXRhZGF0YVNlcnZpY2UuZ2V0VG9rZW5FbmRwb2ludChmYWxzZSkudGhlbih1cmwgPT4ge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJUb2tlbkNsaWVudC5leGNoYW5nZVJlZnJlc2hUb2tlbjogUmVjZWl2ZWQgdG9rZW4gZW5kcG9pbnRcIik7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fanNvblNlcnZpY2UucG9zdEZvcm0odXJsLCBhcmdzKS50aGVuKHJlc3BvbnNlID0+IHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlRva2VuQ2xpZW50LmV4Y2hhbmdlUmVmcmVzaFRva2VuOiByZXNwb25zZSByZWNlaXZlZFwiKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiByZXNwb25zZTtcclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5pbXBvcnQgeyBNZXRhZGF0YVNlcnZpY2UgfSBmcm9tICcuL01ldGFkYXRhU2VydmljZS5qcyc7XHJcbmltcG9ydCB7IEdsb2JhbCB9IGZyb20gJy4vR2xvYmFsLmpzJztcclxuXHJcbmNvbnN0IEFjY2Vzc1Rva2VuVHlwZUhpbnQgPSBcImFjY2Vzc190b2tlblwiO1xyXG5jb25zdCBSZWZyZXNoVG9rZW5UeXBlSGludCA9IFwicmVmcmVzaF90b2tlblwiO1xyXG5cclxuZXhwb3J0IGNsYXNzIFRva2VuUmV2b2NhdGlvbkNsaWVudCB7XHJcbiAgICBjb25zdHJ1Y3RvcihzZXR0aW5ncywgWE1MSHR0cFJlcXVlc3RDdG9yID0gR2xvYmFsLlhNTEh0dHBSZXF1ZXN0LCBNZXRhZGF0YVNlcnZpY2VDdG9yID0gTWV0YWRhdGFTZXJ2aWNlKSB7XHJcbiAgICAgICAgaWYgKCFzZXR0aW5ncykge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJUb2tlblJldm9jYXRpb25DbGllbnQuY3RvcjogTm8gc2V0dGluZ3MgcHJvdmlkZWRcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIk5vIHNldHRpbmdzIHByb3ZpZGVkLlwiKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHRoaXMuX3NldHRpbmdzID0gc2V0dGluZ3M7XHJcbiAgICAgICAgdGhpcy5fWE1MSHR0cFJlcXVlc3RDdG9yID0gWE1MSHR0cFJlcXVlc3RDdG9yO1xyXG4gICAgICAgIHRoaXMuX21ldGFkYXRhU2VydmljZSA9IG5ldyBNZXRhZGF0YVNlcnZpY2VDdG9yKHRoaXMuX3NldHRpbmdzKTtcclxuICAgIH1cclxuXHJcbiAgICByZXZva2UodG9rZW4sIHJlcXVpcmVkLCB0eXBlID0gXCJhY2Nlc3NfdG9rZW5cIikge1xyXG4gICAgICAgIGlmICghdG9rZW4pIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiVG9rZW5SZXZvY2F0aW9uQ2xpZW50LnJldm9rZTogTm8gdG9rZW4gcHJvdmlkZWRcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcIk5vIHRva2VuIHByb3ZpZGVkLlwiKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICh0eXBlICE9PSBBY2Nlc3NUb2tlblR5cGVIaW50ICYmIHR5cGUgIT0gUmVmcmVzaFRva2VuVHlwZUhpbnQpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiVG9rZW5SZXZvY2F0aW9uQ2xpZW50LnJldm9rZTogSW52YWxpZCB0b2tlbiB0eXBlXCIpO1xyXG4gICAgICAgICAgICB0aHJvdyBuZXcgRXJyb3IoXCJJbnZhbGlkIHRva2VuIHR5cGUuXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX21ldGFkYXRhU2VydmljZS5nZXRSZXZvY2F0aW9uRW5kcG9pbnQoKS50aGVuKHVybCA9PiB7XHJcbiAgICAgICAgICAgIGlmICghdXJsKSB7XHJcbiAgICAgICAgICAgICAgICBpZiAocmVxdWlyZWQpIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJUb2tlblJldm9jYXRpb25DbGllbnQucmV2b2tlOiBSZXZvY2F0aW9uIG5vdCBzdXBwb3J0ZWRcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgdGhyb3cgbmV3IEVycm9yKFwiUmV2b2NhdGlvbiBub3Qgc3VwcG9ydGVkXCIpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgIC8vIG5vdCByZXF1aXJlZCwgc28gZG9uJ3QgZXJyb3IgYW5kIGp1c3QgcmV0dXJuXHJcbiAgICAgICAgICAgICAgICByZXR1cm47XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlRva2VuUmV2b2NhdGlvbkNsaWVudC5yZXZva2U6IFJldm9raW5nIFwiICsgdHlwZSk7XHJcbiAgICAgICAgICAgIHZhciBjbGllbnRfaWQgPSB0aGlzLl9zZXR0aW5ncy5jbGllbnRfaWQ7XHJcbiAgICAgICAgICAgIHZhciBjbGllbnRfc2VjcmV0ID0gdGhpcy5fc2V0dGluZ3MuY2xpZW50X3NlY3JldDtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3Jldm9rZSh1cmwsIGNsaWVudF9pZCwgY2xpZW50X3NlY3JldCwgdG9rZW4sIHR5cGUpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIF9yZXZva2UodXJsLCBjbGllbnRfaWQsIGNsaWVudF9zZWNyZXQsIHRva2VuLCB0eXBlKSB7XHJcblxyXG4gICAgICAgIHJldHVybiBuZXcgUHJvbWlzZSgocmVzb2x2ZSwgcmVqZWN0KSA9PiB7XHJcblxyXG4gICAgICAgICAgICB2YXIgeGhyID0gbmV3IHRoaXMuX1hNTEh0dHBSZXF1ZXN0Q3RvcigpO1xyXG4gICAgICAgICAgICB4aHIub3BlbihcIlBPU1RcIiwgdXJsKTtcclxuXHJcbiAgICAgICAgICAgIHhoci5vbmxvYWQgPSAoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJUb2tlblJldm9jYXRpb25DbGllbnQucmV2b2tlOiBIVFRQIHJlc3BvbnNlIHJlY2VpdmVkLCBzdGF0dXNcIiwgeGhyLnN0YXR1cyk7XHJcblxyXG4gICAgICAgICAgICAgICAgaWYgKHhoci5zdGF0dXMgPT09IDIwMCkge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlc29sdmUoKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIHJlamVjdChFcnJvcih4aHIuc3RhdHVzVGV4dCArIFwiIChcIiArIHhoci5zdGF0dXMgKyBcIilcIikpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICB4aHIub25lcnJvciA9ICgpID0+IHsgXHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJUb2tlblJldm9jYXRpb25DbGllbnQucmV2b2tlOiBOZXR3b3JrIEVycm9yLlwiKVxyXG4gICAgICAgICAgICAgICAgcmVqZWN0KFwiTmV0d29yayBFcnJvclwiKTtcclxuICAgICAgICAgICAgfTtcclxuXHJcbiAgICAgICAgICAgIHZhciBib2R5ID0gXCJjbGllbnRfaWQ9XCIgKyBlbmNvZGVVUklDb21wb25lbnQoY2xpZW50X2lkKTtcclxuICAgICAgICAgICAgaWYgKGNsaWVudF9zZWNyZXQpIHtcclxuICAgICAgICAgICAgICAgIGJvZHkgKz0gXCImY2xpZW50X3NlY3JldD1cIiArIGVuY29kZVVSSUNvbXBvbmVudChjbGllbnRfc2VjcmV0KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBib2R5ICs9IFwiJnRva2VuX3R5cGVfaGludD1cIiArIGVuY29kZVVSSUNvbXBvbmVudCh0eXBlKTtcclxuICAgICAgICAgICAgYm9keSArPSBcIiZ0b2tlbj1cIiArIGVuY29kZVVSSUNvbXBvbmVudCh0b2tlbik7XHJcblxyXG4gICAgICAgICAgICB4aHIuc2V0UmVxdWVzdEhlYWRlcihcIkNvbnRlbnQtVHlwZVwiLCBcImFwcGxpY2F0aW9uL3gtd3d3LWZvcm0tdXJsZW5jb2RlZFwiKTtcclxuICAgICAgICAgICAgeGhyLnNlbmQoYm9keSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbn1cclxuIiwiLy8gQ29weXJpZ2h0IChjKSBCcm9jayBBbGxlbiAmIERvbWluaWNrIEJhaWVyLiBBbGwgcmlnaHRzIHJlc2VydmVkLlxyXG4vLyBMaWNlbnNlZCB1bmRlciB0aGUgQXBhY2hlIExpY2Vuc2UsIFZlcnNpb24gMi4wLiBTZWUgTElDRU5TRSBpbiB0aGUgcHJvamVjdCByb290IGZvciBsaWNlbnNlIGluZm9ybWF0aW9uLlxyXG5cclxuaW1wb3J0IHsgTG9nIH0gZnJvbSAnLi9Mb2cuanMnO1xyXG5pbXBvcnQgeyBHbG9iYWwgfSBmcm9tICcuL0dsb2JhbC5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgVXJsVXRpbGl0eSB7XHJcbiAgICBzdGF0aWMgYWRkUXVlcnlQYXJhbSh1cmwsIG5hbWUsIHZhbHVlKSB7XHJcbiAgICAgICAgaWYgKHVybC5pbmRleE9mKCc/JykgPCAwKSB7XHJcbiAgICAgICAgICAgIHVybCArPSBcIj9cIjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmICh1cmxbdXJsLmxlbmd0aCAtIDFdICE9PSBcIj9cIikge1xyXG4gICAgICAgICAgICB1cmwgKz0gXCImXCI7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICB1cmwgKz0gZW5jb2RlVVJJQ29tcG9uZW50KG5hbWUpO1xyXG4gICAgICAgIHVybCArPSBcIj1cIjtcclxuICAgICAgICB1cmwgKz0gZW5jb2RlVVJJQ29tcG9uZW50KHZhbHVlKTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHVybDtcclxuICAgIH1cclxuXHJcbiAgICBzdGF0aWMgcGFyc2VVcmxGcmFnbWVudCh2YWx1ZSwgZGVsaW1pdGVyID0gXCIjXCIsIGdsb2JhbCA9IEdsb2JhbCkge1xyXG4gICAgICAgIGlmICh0eXBlb2YgdmFsdWUgIT09ICdzdHJpbmcnKXtcclxuICAgICAgICAgICAgdmFsdWUgPSBnbG9iYWwubG9jYXRpb24uaHJlZjtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHZhciBpZHggPSB2YWx1ZS5sYXN0SW5kZXhPZihkZWxpbWl0ZXIpO1xyXG4gICAgICAgIGlmIChpZHggPj0gMCkge1xyXG4gICAgICAgICAgICB2YWx1ZSA9IHZhbHVlLnN1YnN0cihpZHggKyAxKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGlmIChkZWxpbWl0ZXIgPT09IFwiP1wiKSB7XHJcbiAgICAgICAgICAgIC8vIGlmIHdlJ3JlIGRvaW5nIHF1ZXJ5LCB0aGVuIHN0cmlwIG9mZiBoYXNoIGZyYWdtZW50IGJlZm9yZSB3ZSBwYXJzZVxyXG4gICAgICAgICAgICBpZHggPSB2YWx1ZS5pbmRleE9mKCcjJyk7XHJcbiAgICAgICAgICAgIGlmIChpZHggPj0gMCkge1xyXG4gICAgICAgICAgICAgICAgdmFsdWUgPSB2YWx1ZS5zdWJzdHIoMCwgaWR4KTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdmFyIHBhcmFtcyA9IHt9LFxyXG4gICAgICAgICAgICByZWdleCA9IC8oW14mPV0rKT0oW14mXSopL2csXHJcbiAgICAgICAgICAgIG07XHJcblxyXG4gICAgICAgIHZhciBjb3VudGVyID0gMDtcclxuICAgICAgICB3aGlsZSAobSA9IHJlZ2V4LmV4ZWModmFsdWUpKSB7XHJcbiAgICAgICAgICAgIHBhcmFtc1tkZWNvZGVVUklDb21wb25lbnQobVsxXSldID0gZGVjb2RlVVJJQ29tcG9uZW50KG1bMl0pO1xyXG4gICAgICAgICAgICBpZiAoY291bnRlcisrID4gNTApIHtcclxuICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlVybFV0aWxpdHkucGFyc2VVcmxGcmFnbWVudDogcmVzcG9uc2UgZXhjZWVkZWQgZXhwZWN0ZWQgbnVtYmVyIG9mIHBhcmFtZXRlcnNcIiwgdmFsdWUpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHtcclxuICAgICAgICAgICAgICAgICAgICBlcnJvcjogXCJSZXNwb25zZSBleGNlZWRlZCBleHBlY3RlZCBudW1iZXIgb2YgcGFyYW1ldGVyc1wiXHJcbiAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBmb3IgKHZhciBwcm9wIGluIHBhcmFtcykge1xyXG4gICAgICAgICAgICByZXR1cm4gcGFyYW1zO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHt9O1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBVc2VyIHtcclxuICAgIGNvbnN0cnVjdG9yKHtpZF90b2tlbiwgc2Vzc2lvbl9zdGF0ZSwgYWNjZXNzX3Rva2VuLCByZWZyZXNoX3Rva2VuLCB0b2tlbl90eXBlLCBzY29wZSwgcHJvZmlsZSwgZXhwaXJlc19hdCwgc3RhdGV9KSB7XHJcbiAgICAgICAgdGhpcy5pZF90b2tlbiA9IGlkX3Rva2VuO1xyXG4gICAgICAgIHRoaXMuc2Vzc2lvbl9zdGF0ZSA9IHNlc3Npb25fc3RhdGU7XHJcbiAgICAgICAgdGhpcy5hY2Nlc3NfdG9rZW4gPSBhY2Nlc3NfdG9rZW47XHJcbiAgICAgICAgdGhpcy5yZWZyZXNoX3Rva2VuID0gcmVmcmVzaF90b2tlbjtcclxuICAgICAgICB0aGlzLnRva2VuX3R5cGUgPSB0b2tlbl90eXBlO1xyXG4gICAgICAgIHRoaXMuc2NvcGUgPSBzY29wZTtcclxuICAgICAgICB0aGlzLnByb2ZpbGUgPSBwcm9maWxlO1xyXG4gICAgICAgIHRoaXMuZXhwaXJlc19hdCA9IGV4cGlyZXNfYXQ7XHJcbiAgICAgICAgdGhpcy5zdGF0ZSA9IHN0YXRlO1xyXG4gICAgfVxyXG5cclxuICAgIGdldCBleHBpcmVzX2luKCkge1xyXG4gICAgICAgIGlmICh0aGlzLmV4cGlyZXNfYXQpIHtcclxuICAgICAgICAgICAgbGV0IG5vdyA9IHBhcnNlSW50KERhdGUubm93KCkgLyAxMDAwKTtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuZXhwaXJlc19hdCAtIG5vdztcclxuICAgICAgICB9XHJcbiAgICAgICAgcmV0dXJuIHVuZGVmaW5lZDtcclxuICAgIH1cclxuICAgIHNldCBleHBpcmVzX2luKHZhbHVlKSB7XHJcbiAgICAgICAgbGV0IGV4cGlyZXNfaW4gPSBwYXJzZUludCh2YWx1ZSk7XHJcbiAgICAgICAgaWYgKHR5cGVvZiBleHBpcmVzX2luID09PSAnbnVtYmVyJyAmJiBleHBpcmVzX2luID4gMCkge1xyXG4gICAgICAgICAgICBsZXQgbm93ID0gcGFyc2VJbnQoRGF0ZS5ub3coKSAvIDEwMDApO1xyXG4gICAgICAgICAgICB0aGlzLmV4cGlyZXNfYXQgPSBub3cgKyBleHBpcmVzX2luO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBnZXQgZXhwaXJlZCgpIHtcclxuICAgICAgICBsZXQgZXhwaXJlc19pbiA9IHRoaXMuZXhwaXJlc19pbjtcclxuICAgICAgICBpZiAoZXhwaXJlc19pbiAhPT0gdW5kZWZpbmVkKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBleHBpcmVzX2luIDw9IDA7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIHJldHVybiB1bmRlZmluZWQ7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IHNjb3BlcygpIHtcclxuICAgICAgICByZXR1cm4gKHRoaXMuc2NvcGUgfHwgXCJcIikuc3BsaXQoXCIgXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIHRvU3RvcmFnZVN0cmluZygpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJVc2VyLnRvU3RvcmFnZVN0cmluZ1wiKTtcclxuICAgICAgICByZXR1cm4gSlNPTi5zdHJpbmdpZnkoe1xyXG4gICAgICAgICAgICBpZF90b2tlbjogdGhpcy5pZF90b2tlbixcclxuICAgICAgICAgICAgc2Vzc2lvbl9zdGF0ZTogdGhpcy5zZXNzaW9uX3N0YXRlLFxyXG4gICAgICAgICAgICBhY2Nlc3NfdG9rZW46IHRoaXMuYWNjZXNzX3Rva2VuLFxyXG4gICAgICAgICAgICByZWZyZXNoX3Rva2VuOiB0aGlzLnJlZnJlc2hfdG9rZW4sXHJcbiAgICAgICAgICAgIHRva2VuX3R5cGU6IHRoaXMudG9rZW5fdHlwZSxcclxuICAgICAgICAgICAgc2NvcGU6IHRoaXMuc2NvcGUsXHJcbiAgICAgICAgICAgIHByb2ZpbGU6IHRoaXMucHJvZmlsZSxcclxuICAgICAgICAgICAgZXhwaXJlc19hdDogdGhpcy5leHBpcmVzX2F0XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhdGljIGZyb21TdG9yYWdlU3RyaW5nKHN0b3JhZ2VTdHJpbmcpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJVc2VyLmZyb21TdG9yYWdlU3RyaW5nXCIpO1xyXG4gICAgICAgIHJldHVybiBuZXcgVXNlcihKU09OLnBhcnNlKHN0b3JhZ2VTdHJpbmcpKTtcclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBKc29uU2VydmljZSB9IGZyb20gJy4vSnNvblNlcnZpY2UuanMnO1xyXG5pbXBvcnQgeyBNZXRhZGF0YVNlcnZpY2UgfSBmcm9tICcuL01ldGFkYXRhU2VydmljZS5qcyc7XHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuaW1wb3J0IHsgSm9zZVV0aWwgfSBmcm9tICcuL0pvc2VVdGlsLmpzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBVc2VySW5mb1NlcnZpY2Uge1xyXG4gICAgY29uc3RydWN0b3IoXHJcbiAgICAgICAgc2V0dGluZ3MsIFxyXG4gICAgICAgIEpzb25TZXJ2aWNlQ3RvciA9IEpzb25TZXJ2aWNlLCBcclxuICAgICAgICBNZXRhZGF0YVNlcnZpY2VDdG9yID0gTWV0YWRhdGFTZXJ2aWNlLCBcclxuICAgICAgICBqb3NlVXRpbCA9IEpvc2VVdGlsXHJcbiAgICApIHtcclxuICAgICAgICBpZiAoIXNldHRpbmdzKSB7XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlVzZXJJbmZvU2VydmljZS5jdG9yOiBObyBzZXR0aW5ncyBwYXNzZWRcIik7XHJcbiAgICAgICAgICAgIHRocm93IG5ldyBFcnJvcihcInNldHRpbmdzXCIpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fc2V0dGluZ3MgPSBzZXR0aW5ncztcclxuICAgICAgICB0aGlzLl9qc29uU2VydmljZSA9IG5ldyBKc29uU2VydmljZUN0b3IodW5kZWZpbmVkLCB1bmRlZmluZWQsIHRoaXMuX2dldENsYWltc0Zyb21Kd3QuYmluZCh0aGlzKSk7XHJcbiAgICAgICAgdGhpcy5fbWV0YWRhdGFTZXJ2aWNlID0gbmV3IE1ldGFkYXRhU2VydmljZUN0b3IodGhpcy5fc2V0dGluZ3MpO1xyXG4gICAgICAgIHRoaXMuX2pvc2VVdGlsID0gam9zZVV0aWw7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0Q2xhaW1zKHRva2VuKSB7XHJcbiAgICAgICAgaWYgKCF0b2tlbikge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJVc2VySW5mb1NlcnZpY2UuZ2V0Q2xhaW1zOiBObyB0b2tlbiBwYXNzZWRcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJBIHRva2VuIGlzIHJlcXVpcmVkXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIHJldHVybiB0aGlzLl9tZXRhZGF0YVNlcnZpY2UuZ2V0VXNlckluZm9FbmRwb2ludCgpLnRoZW4odXJsID0+IHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlckluZm9TZXJ2aWNlLmdldENsYWltczogcmVjZWl2ZWQgdXNlcmluZm8gdXJsXCIsIHVybCk7XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fanNvblNlcnZpY2UuZ2V0SnNvbih1cmwsIHRva2VuKS50aGVuKGNsYWltcyA9PiB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VySW5mb1NlcnZpY2UuZ2V0Q2xhaW1zOiBjbGFpbXMgcmVjZWl2ZWRcIiwgY2xhaW1zKTtcclxuICAgICAgICAgICAgICAgIHJldHVybiBjbGFpbXM7XHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIF9nZXRDbGFpbXNGcm9tSnd0KHJlcSkge1xyXG4gICAgICAgIHRyeSB7XHJcbiAgICAgICAgICAgIGxldCBqd3QgPSB0aGlzLl9qb3NlVXRpbC5wYXJzZUp3dChyZXEucmVzcG9uc2VUZXh0KTtcclxuICAgICAgICAgICAgaWYgKCFqd3QgfHwgIWp3dC5oZWFkZXIgfHwgIWp3dC5wYXlsb2FkKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJVc2VySW5mb1NlcnZpY2UuX2dldENsYWltc0Zyb21Kd3Q6IEZhaWxlZCB0byBwYXJzZSBKV1RcIiwgand0KTtcclxuICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJGYWlsZWQgdG8gcGFyc2UgaWRfdG9rZW5cIikpO1xyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICB2YXIga2lkID0gand0LmhlYWRlci5raWQ7XHJcblxyXG4gICAgICAgICAgICBsZXQgaXNzdWVyUHJvbWlzZTtcclxuICAgICAgICAgICAgc3dpdGNoICh0aGlzLl9zZXR0aW5ncy51c2VySW5mb0p3dElzc3Vlcikge1xyXG4gICAgICAgICAgICAgICAgY2FzZSAnT1AnOlxyXG4gICAgICAgICAgICAgICAgICAgIGlzc3VlclByb21pc2UgPSB0aGlzLl9tZXRhZGF0YVNlcnZpY2UuZ2V0SXNzdWVyKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgICAgICBjYXNlICdBTlknOlxyXG4gICAgICAgICAgICAgICAgICAgIGlzc3VlclByb21pc2UgPSBQcm9taXNlLnJlc29sdmUoand0LnBheWxvYWQuaXNzKTtcclxuICAgICAgICAgICAgICAgICAgICBicmVhaztcclxuICAgICAgICAgICAgICAgIGRlZmF1bHQ6XHJcbiAgICAgICAgICAgICAgICAgICAgaXNzdWVyUHJvbWlzZSA9IFByb21pc2UucmVzb2x2ZSh0aGlzLl9zZXR0aW5ncy51c2VySW5mb0p3dElzc3Vlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgYnJlYWs7XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHJldHVybiBpc3N1ZXJQcm9taXNlLnRoZW4oaXNzdWVyID0+IHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJJbmZvU2VydmljZS5fZ2V0Q2xhaW1zRnJvbUp3dDogUmVjZWl2ZWQgaXNzdWVyOlwiICsgaXNzdWVyKTtcclxuXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fbWV0YWRhdGFTZXJ2aWNlLmdldFNpZ25pbmdLZXlzKCkudGhlbihrZXlzID0+IHtcclxuICAgICAgICAgICAgICAgICAgICBpZiAoIWtleXMpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiVXNlckluZm9TZXJ2aWNlLl9nZXRDbGFpbXNGcm9tSnd0OiBObyBzaWduaW5nIGtleXMgZnJvbSBtZXRhZGF0YVwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5vIHNpZ25pbmcga2V5cyBmcm9tIG1ldGFkYXRhXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJJbmZvU2VydmljZS5fZ2V0Q2xhaW1zRnJvbUp3dDogUmVjZWl2ZWQgc2lnbmluZyBrZXlzXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIGxldCBrZXk7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKCFraWQpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAga2V5cyA9IHRoaXMuX2ZpbHRlckJ5QWxnKGtleXMsIGp3dC5oZWFkZXIuYWxnKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGlmIChrZXlzLmxlbmd0aCA+IDEpIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlVzZXJJbmZvU2VydmljZS5fZ2V0Q2xhaW1zRnJvbUp3dDogTm8ga2lkIGZvdW5kIGluIGlkX3Rva2VuIGFuZCBtb3JlIHRoYW4gb25lIGtleSBmb3VuZCBpbiBtZXRhZGF0YVwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJObyBraWQgZm91bmQgaW4gaWRfdG9rZW4gYW5kIG1vcmUgdGhhbiBvbmUga2V5IGZvdW5kIGluIG1ldGFkYXRhXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIGtpZCBpcyBtYW5kYXRvcnkgb25seSB3aGVuIHRoZXJlIGFyZSBtdWx0aXBsZSBrZXlzIGluIHRoZSByZWZlcmVuY2VkIEpXSyBTZXQgZG9jdW1lbnRcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIC8vIHNlZSBodHRwOi8vb3BlbmlkLm5ldC9zcGVjcy9vcGVuaWQtY29ubmVjdC1jb3JlLTFfMC5odG1sI1NpZ25pbmdcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIGtleSA9IGtleXNbMF07XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGtleSA9IGtleXMuZmlsdGVyKGtleSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ga2V5LmtpZCA9PT0ga2lkO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB9KVswXTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGlmICgha2V5KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlVzZXJJbmZvU2VydmljZS5fZ2V0Q2xhaW1zRnJvbUp3dDogTm8ga2V5IG1hdGNoaW5nIGtpZCBvciBhbGcgZm91bmQgaW4gc2lnbmluZyBrZXlzXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiTm8ga2V5IG1hdGNoaW5nIGtpZCBvciBhbGcgZm91bmQgaW4gc2lnbmluZyBrZXlzXCIpKTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIGxldCBhdWRpZW5jZSA9IHRoaXMuX3NldHRpbmdzLmNsaWVudF9pZDtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgbGV0IGNsb2NrU2tld0luU2Vjb25kcyA9IHRoaXMuX3NldHRpbmdzLmNsb2NrU2tldztcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VySW5mb1NlcnZpY2UuX2dldENsYWltc0Zyb21Kd3Q6IFZhbGlkYWluZyBKV1Q7IHVzaW5nIGNsb2NrIHNrZXcgKGluIHNlY29uZHMpIG9mOiBcIiwgY2xvY2tTa2V3SW5TZWNvbmRzKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2pvc2VVdGlsLnZhbGlkYXRlSnd0KHJlcS5yZXNwb25zZVRleHQsIGtleSwgaXNzdWVyLCBhdWRpZW5jZSwgY2xvY2tTa2V3SW5TZWNvbmRzLCB1bmRlZmluZWQsIHRydWUpLnRoZW4oKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VySW5mb1NlcnZpY2UuX2dldENsYWltc0Zyb21Kd3Q6IEpXVCB2YWxpZGF0aW9uIHN1Y2Nlc3NmdWxcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiBqd3QucGF5bG9hZDtcclxuICAgICAgICAgICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH1cclxuICAgICAgICBjYXRjaCAoZSkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJVc2VySW5mb1NlcnZpY2UuX2dldENsYWltc0Zyb21Kd3Q6IEVycm9yIHBhcnNpbmcgSldUIHJlc3BvbnNlXCIsIGUubWVzc2FnZSk7XHJcbiAgICAgICAgICAgIHJlamVjdChlKTtcclxuICAgICAgICAgICAgcmV0dXJuO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxuXHJcbiAgICBfZmlsdGVyQnlBbGcoa2V5cywgYWxnKSB7XHJcbiAgICAgICAgdmFyIGt0eSA9IG51bGw7XHJcbiAgICAgICAgaWYgKGFsZy5zdGFydHNXaXRoKFwiUlNcIikpIHtcclxuICAgICAgICAgICAga3R5ID0gXCJSU0FcIjtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSBpZiAoYWxnLnN0YXJ0c1dpdGgoXCJQU1wiKSkge1xyXG4gICAgICAgICAgICBrdHkgPSBcIlBTXCI7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2UgaWYgKGFsZy5zdGFydHNXaXRoKFwiRVNcIikpIHtcclxuICAgICAgICAgICAga3R5ID0gXCJFQ1wiO1xyXG4gICAgICAgIH1cclxuICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlckluZm9TZXJ2aWNlLl9maWx0ZXJCeUFsZzogYWxnIG5vdCBzdXBwb3J0ZWQ6IFwiLCBhbGcpO1xyXG4gICAgICAgICAgICByZXR1cm4gW107XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBMb2cuZGVidWcoXCJVc2VySW5mb1NlcnZpY2UuX2ZpbHRlckJ5QWxnOiBMb29raW5nIGZvciBrZXlzIHRoYXQgbWF0Y2gga3R5OiBcIiwga3R5KTtcclxuXHJcbiAgICAgICAga2V5cyA9IGtleXMuZmlsdGVyKGtleSA9PiB7XHJcbiAgICAgICAgICAgIHJldHVybiBrZXkua3R5ID09PSBrdHk7XHJcbiAgICAgICAgfSk7XHJcblxyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJJbmZvU2VydmljZS5fZmlsdGVyQnlBbGc6IE51bWJlciBvZiBrZXlzIHRoYXQgbWF0Y2gga3R5OiBcIiwga3R5LCBrZXlzLmxlbmd0aCk7XHJcblxyXG4gICAgICAgIHJldHVybiBrZXlzO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuaW1wb3J0IHsgT2lkY0NsaWVudCB9IGZyb20gJy4vT2lkY0NsaWVudC5qcyc7XHJcbmltcG9ydCB7IFVzZXJNYW5hZ2VyU2V0dGluZ3MgfSBmcm9tICcuL1VzZXJNYW5hZ2VyU2V0dGluZ3MuanMnO1xyXG5pbXBvcnQgeyBVc2VyIH0gZnJvbSAnLi9Vc2VyLmpzJztcclxuaW1wb3J0IHsgVXNlck1hbmFnZXJFdmVudHMgfSBmcm9tICcuL1VzZXJNYW5hZ2VyRXZlbnRzLmpzJztcclxuaW1wb3J0IHsgU2lsZW50UmVuZXdTZXJ2aWNlIH0gZnJvbSAnLi9TaWxlbnRSZW5ld1NlcnZpY2UuanMnO1xyXG5pbXBvcnQgeyBTZXNzaW9uTW9uaXRvciB9IGZyb20gJy4vU2Vzc2lvbk1vbml0b3IuanMnO1xyXG5pbXBvcnQgeyBUb2tlblJldm9jYXRpb25DbGllbnQgfSBmcm9tICcuL1Rva2VuUmV2b2NhdGlvbkNsaWVudC5qcyc7XHJcbmltcG9ydCB7IFRva2VuQ2xpZW50IH0gZnJvbSAnLi9Ub2tlbkNsaWVudC5qcyc7XHJcbmltcG9ydCB7IEpvc2VVdGlsIH0gZnJvbSAnLi9Kb3NlVXRpbC5qcyc7XHJcblxyXG5cclxuZXhwb3J0IGNsYXNzIFVzZXJNYW5hZ2VyIGV4dGVuZHMgT2lkY0NsaWVudCB7XHJcbiAgICBjb25zdHJ1Y3RvcihzZXR0aW5ncyA9IHt9LFxyXG4gICAgICAgIFNpbGVudFJlbmV3U2VydmljZUN0b3IgPSBTaWxlbnRSZW5ld1NlcnZpY2UsXHJcbiAgICAgICAgU2Vzc2lvbk1vbml0b3JDdG9yID0gU2Vzc2lvbk1vbml0b3IsXHJcbiAgICAgICAgVG9rZW5SZXZvY2F0aW9uQ2xpZW50Q3RvciA9IFRva2VuUmV2b2NhdGlvbkNsaWVudCxcclxuICAgICAgICBUb2tlbkNsaWVudEN0b3IgPSBUb2tlbkNsaWVudCxcclxuICAgICAgICBqb3NlVXRpbCA9IEpvc2VVdGlsXHJcbiAgICApIHtcclxuXHJcbiAgICAgICAgaWYgKCEoc2V0dGluZ3MgaW5zdGFuY2VvZiBVc2VyTWFuYWdlclNldHRpbmdzKSkge1xyXG4gICAgICAgICAgICBzZXR0aW5ncyA9IG5ldyBVc2VyTWFuYWdlclNldHRpbmdzKHNldHRpbmdzKTtcclxuICAgICAgICB9XHJcbiAgICAgICAgc3VwZXIoc2V0dGluZ3MpO1xyXG5cclxuICAgICAgICB0aGlzLl9ldmVudHMgPSBuZXcgVXNlck1hbmFnZXJFdmVudHMoc2V0dGluZ3MpO1xyXG4gICAgICAgIHRoaXMuX3NpbGVudFJlbmV3U2VydmljZSA9IG5ldyBTaWxlbnRSZW5ld1NlcnZpY2VDdG9yKHRoaXMpO1xyXG5cclxuICAgICAgICAvLyBvcmRlciBpcyBpbXBvcnRhbnQgZm9yIHRoZSBmb2xsb3dpbmcgcHJvcGVydGllczsgdGhlc2Ugc2VydmljZXMgZGVwZW5kIHVwb24gdGhlIGV2ZW50cy5cclxuICAgICAgICBpZiAodGhpcy5zZXR0aW5ncy5hdXRvbWF0aWNTaWxlbnRSZW5ldykge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VyTWFuYWdlci5jdG9yOiBhdXRvbWF0aWNTaWxlbnRSZW5ldyBpcyBjb25maWd1cmVkLCBzZXR0aW5nIHVwIHNpbGVudCByZW5ld1wiKTtcclxuICAgICAgICAgICAgdGhpcy5zdGFydFNpbGVudFJlbmV3KCk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBpZiAodGhpcy5zZXR0aW5ncy5tb25pdG9yU2Vzc2lvbikge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VyTWFuYWdlci5jdG9yOiBtb25pdG9yU2Vzc2lvbiBpcyBjb25maWd1cmVkLCBzZXR0aW5nIHVwIHNlc3Npb24gbW9uaXRvclwiKTtcclxuICAgICAgICAgICAgdGhpcy5fc2Vzc2lvbk1vbml0b3IgPSBuZXcgU2Vzc2lvbk1vbml0b3JDdG9yKHRoaXMpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgdGhpcy5fdG9rZW5SZXZvY2F0aW9uQ2xpZW50ID0gbmV3IFRva2VuUmV2b2NhdGlvbkNsaWVudEN0b3IodGhpcy5fc2V0dGluZ3MpO1xyXG4gICAgICAgIHRoaXMuX3Rva2VuQ2xpZW50ID0gbmV3IFRva2VuQ2xpZW50Q3Rvcih0aGlzLl9zZXR0aW5ncyk7XHJcbiAgICAgICAgdGhpcy5fam9zZVV0aWwgPSBqb3NlVXRpbDtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgX3JlZGlyZWN0TmF2aWdhdG9yKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLnJlZGlyZWN0TmF2aWdhdG9yO1xyXG4gICAgfVxyXG4gICAgZ2V0IF9wb3B1cE5hdmlnYXRvcigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5zZXR0aW5ncy5wb3B1cE5hdmlnYXRvcjtcclxuICAgIH1cclxuICAgIGdldCBfaWZyYW1lTmF2aWdhdG9yKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLmlmcmFtZU5hdmlnYXRvcjtcclxuICAgIH1cclxuICAgIGdldCBfdXNlclN0b3JlKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnNldHRpbmdzLnVzZXJTdG9yZTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgZXZlbnRzKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9ldmVudHM7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0VXNlcigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fbG9hZFVzZXIoKS50aGVuKHVzZXIgPT4ge1xyXG4gICAgICAgICAgICBpZiAodXNlcikge1xyXG4gICAgICAgICAgICAgICAgTG9nLmluZm8oXCJVc2VyTWFuYWdlci5nZXRVc2VyOiB1c2VyIGxvYWRlZFwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICB0aGlzLl9ldmVudHMubG9hZCh1c2VyLCBmYWxzZSk7XHJcblxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHVzZXI7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLmdldFVzZXI6IHVzZXIgbm90IGZvdW5kIGluIHN0b3JhZ2VcIik7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gbnVsbDtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHJlbW92ZVVzZXIoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuc3RvcmVVc2VyKG51bGwpLnRoZW4oKCkgPT4ge1xyXG4gICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnJlbW92ZVVzZXI6IHVzZXIgcmVtb3ZlZCBmcm9tIHN0b3JhZ2VcIik7XHJcbiAgICAgICAgICAgIHRoaXMuX2V2ZW50cy51bmxvYWQoKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBzaWduaW5SZWRpcmVjdChhcmdzID0ge30pIHtcclxuICAgICAgICBhcmdzID0gT2JqZWN0LmFzc2lnbih7fSwgYXJncyk7XHJcblxyXG4gICAgICAgIGFyZ3MucmVxdWVzdF90eXBlID0gXCJzaTpyXCI7XHJcbiAgICAgICAgbGV0IG5hdlBhcmFtcyA9IHtcclxuICAgICAgICAgICAgdXNlUmVwbGFjZVRvTmF2aWdhdGUgOiBhcmdzLnVzZVJlcGxhY2VUb05hdmlnYXRlXHJcbiAgICAgICAgfTtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc2lnbmluU3RhcnQoYXJncywgdGhpcy5fcmVkaXJlY3ROYXZpZ2F0b3IsIG5hdlBhcmFtcykudGhlbigoKT0+e1xyXG4gICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnNpZ25pblJlZGlyZWN0OiBzdWNjZXNzZnVsXCIpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgc2lnbmluUmVkaXJlY3RDYWxsYmFjayh1cmwpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc2lnbmluRW5kKHVybCB8fCB0aGlzLl9yZWRpcmVjdE5hdmlnYXRvci51cmwpLnRoZW4odXNlciA9PiB7XHJcbiAgICAgICAgICAgIGlmICh1c2VyLnByb2ZpbGUgJiYgdXNlci5wcm9maWxlLnN1Yikge1xyXG4gICAgICAgICAgICAgICAgTG9nLmluZm8oXCJVc2VyTWFuYWdlci5zaWduaW5SZWRpcmVjdENhbGxiYWNrOiBzdWNjZXNzZnVsLCBzaWduZWQgaW4gc3ViOiBcIiwgdXNlci5wcm9maWxlLnN1Yik7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnNpZ25pblJlZGlyZWN0Q2FsbGJhY2s6IG5vIHN1YlwiKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHVzZXI7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgc2lnbmluUG9wdXAoYXJncyA9IHt9KSB7XHJcbiAgICAgICAgYXJncyA9IE9iamVjdC5hc3NpZ24oe30sIGFyZ3MpO1xyXG5cclxuICAgICAgICBhcmdzLnJlcXVlc3RfdHlwZSA9IFwic2k6cFwiO1xyXG4gICAgICAgIGxldCB1cmwgPSBhcmdzLnJlZGlyZWN0X3VyaSB8fCB0aGlzLnNldHRpbmdzLnBvcHVwX3JlZGlyZWN0X3VyaSB8fCB0aGlzLnNldHRpbmdzLnJlZGlyZWN0X3VyaTtcclxuICAgICAgICBpZiAoIXVybCkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJVc2VyTWFuYWdlci5zaWduaW5Qb3B1cDogTm8gcG9wdXBfcmVkaXJlY3RfdXJpIG9yIHJlZGlyZWN0X3VyaSBjb25maWd1cmVkXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiTm8gcG9wdXBfcmVkaXJlY3RfdXJpIG9yIHJlZGlyZWN0X3VyaSBjb25maWd1cmVkXCIpKTtcclxuICAgICAgICB9XHJcblxyXG4gICAgICAgIGFyZ3MucmVkaXJlY3RfdXJpID0gdXJsO1xyXG4gICAgICAgIGFyZ3MuZGlzcGxheSA9IFwicG9wdXBcIjtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3NpZ25pbihhcmdzLCB0aGlzLl9wb3B1cE5hdmlnYXRvciwge1xyXG4gICAgICAgICAgICBzdGFydFVybDogdXJsLFxyXG4gICAgICAgICAgICBwb3B1cFdpbmRvd0ZlYXR1cmVzOiBhcmdzLnBvcHVwV2luZG93RmVhdHVyZXMgfHwgdGhpcy5zZXR0aW5ncy5wb3B1cFdpbmRvd0ZlYXR1cmVzLFxyXG4gICAgICAgICAgICBwb3B1cFdpbmRvd1RhcmdldDogYXJncy5wb3B1cFdpbmRvd1RhcmdldCB8fCB0aGlzLnNldHRpbmdzLnBvcHVwV2luZG93VGFyZ2V0XHJcbiAgICAgICAgfSkudGhlbih1c2VyID0+IHtcclxuICAgICAgICAgICAgaWYgKHVzZXIpIHtcclxuICAgICAgICAgICAgICAgIGlmICh1c2VyLnByb2ZpbGUgJiYgdXNlci5wcm9maWxlLnN1Yikge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5pbmZvKFwiVXNlck1hbmFnZXIuc2lnbmluUG9wdXA6IHNpZ25pblBvcHVwIHN1Y2Nlc3NmdWwsIHNpZ25lZCBpbiBzdWI6IFwiLCB1c2VyLnByb2ZpbGUuc3ViKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5pbmZvKFwiVXNlck1hbmFnZXIuc2lnbmluUG9wdXA6IG5vIHN1YlwiKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHVzZXI7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBzaWduaW5Qb3B1cENhbGxiYWNrKHVybCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9zaWduaW5DYWxsYmFjayh1cmwsIHRoaXMuX3BvcHVwTmF2aWdhdG9yKS50aGVuKHVzZXIgPT4ge1xyXG4gICAgICAgICAgICBpZiAodXNlcikge1xyXG4gICAgICAgICAgICAgICAgaWYgKHVzZXIucHJvZmlsZSAmJiB1c2VyLnByb2ZpbGUuc3ViKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmluZm8oXCJVc2VyTWFuYWdlci5zaWduaW5Qb3B1cENhbGxiYWNrOiBzdWNjZXNzZnVsLCBzaWduZWQgaW4gc3ViOiBcIiwgdXNlci5wcm9maWxlLnN1Yik7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnNpZ25pblBvcHVwQ2FsbGJhY2s6IG5vIHN1YlwiKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHVzZXI7XHJcbiAgICAgICAgfSkuY2F0Y2goZXJyPT57XHJcbiAgICAgICAgICAgIExvZy5lcnJvcihcIlVzZXJNYW5hZ2VyLnNpZ25pblBvcHVwQ2FsbGJhY2sgZXJyb3I6IFwiICsgZXJyICYmIGVyci5tZXNzYWdlKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBzaWduaW5TaWxlbnQoYXJncyA9IHt9KSB7XHJcbiAgICAgICAgYXJncyA9IE9iamVjdC5hc3NpZ24oe30sIGFyZ3MpO1xyXG5cclxuICAgICAgICBhcmdzLnJlcXVlc3RfdHlwZSA9IFwic2k6c1wiO1xyXG4gICAgICAgIC8vIGZpcnN0IGRldGVybWluZSBpZiB3ZSBoYXZlIGEgcmVmcmVzaCB0b2tlbiwgb3IgbmVlZCB0byB1c2UgaWZyYW1lXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2xvYWRVc2VyKCkudGhlbih1c2VyID0+IHtcclxuICAgICAgICAgICAgaWYgKHVzZXIgJiYgdXNlci5yZWZyZXNoX3Rva2VuKSB7XHJcbiAgICAgICAgICAgICAgICBhcmdzLnJlZnJlc2hfdG9rZW4gPSB1c2VyLnJlZnJlc2hfdG9rZW47XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fdXNlUmVmcmVzaFRva2VuKGFyZ3MpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgYXJncy5pZF90b2tlbl9oaW50ID0gYXJncy5pZF90b2tlbl9oaW50IHx8ICh0aGlzLnNldHRpbmdzLmluY2x1ZGVJZFRva2VuSW5TaWxlbnRSZW5ldyAmJiB1c2VyICYmIHVzZXIuaWRfdG9rZW4pO1xyXG4gICAgICAgICAgICAgICAgaWYgKHVzZXIgJiYgdGhpcy5fc2V0dGluZ3MudmFsaWRhdGVTdWJPblNpbGVudFJlbmV3KSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIuc2lnbmluU2lsZW50LCBzdWJqZWN0IHByaW9yIHRvIHNpbGVudCByZW5ldzogXCIsIHVzZXIucHJvZmlsZS5zdWIpO1xyXG4gICAgICAgICAgICAgICAgICAgIGFyZ3MuY3VycmVudF9zdWIgPSB1c2VyLnByb2ZpbGUuc3ViO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3NpZ25pblNpbGVudElmcmFtZShhcmdzKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIF91c2VSZWZyZXNoVG9rZW4oYXJncyA9IHt9KSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Rva2VuQ2xpZW50LmV4Y2hhbmdlUmVmcmVzaFRva2VuKGFyZ3MpLnRoZW4ocmVzdWx0ID0+IHtcclxuICAgICAgICAgICAgaWYgKCFyZXN1bHQpIHtcclxuICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlVzZXJNYW5hZ2VyLl91c2VSZWZyZXNoVG9rZW46IE5vIHJlc3BvbnNlIHJldHVybmVkIGZyb20gdG9rZW4gZW5kcG9pbnRcIik7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoXCJObyByZXNwb25zZSByZXR1cm5lZCBmcm9tIHRva2VuIGVuZHBvaW50XCIpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGlmICghcmVzdWx0LmFjY2Vzc190b2tlbikge1xyXG4gICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiVXNlck1hbmFnZXIuX3VzZVJlZnJlc2hUb2tlbjogTm8gYWNjZXNzIHRva2VuIHJldHVybmVkIGZyb20gdG9rZW4gZW5kcG9pbnRcIik7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QoXCJObyBhY2Nlc3MgdG9rZW4gcmV0dXJuZWQgZnJvbSB0b2tlbiBlbmRwb2ludFwiKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2xvYWRVc2VyKCkudGhlbih1c2VyID0+IHtcclxuICAgICAgICAgICAgICAgIGlmICh1c2VyKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgbGV0IGlkVG9rZW5WYWxpZGF0aW9uID0gUHJvbWlzZS5yZXNvbHZlKCk7XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKHJlc3VsdC5pZF90b2tlbikge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBpZFRva2VuVmFsaWRhdGlvbiA9IHRoaXMuX3ZhbGlkYXRlSWRUb2tlbkZyb21Ub2tlblJlZnJlc2hUb2tlbih1c2VyLnByb2ZpbGUsIHJlc3VsdC5pZF90b2tlbik7XHJcbiAgICAgICAgICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gaWRUb2tlblZhbGlkYXRpb24udGhlbigoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLl91c2VSZWZyZXNoVG9rZW46IHJlZnJlc2ggdG9rZW4gcmVzcG9uc2Ugc3VjY2Vzc1wiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdXNlci5pZF90b2tlbiA9IHJlc3VsdC5pZF90b2tlbjtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdXNlci5hY2Nlc3NfdG9rZW4gPSByZXN1bHQuYWNjZXNzX3Rva2VuO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICB1c2VyLnJlZnJlc2hfdG9rZW4gPSByZXN1bHQucmVmcmVzaF90b2tlbiB8fCB1c2VyLnJlZnJlc2hfdG9rZW47XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHVzZXIuZXhwaXJlc19pbiA9IHJlc3VsdC5leHBpcmVzX2luO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuc3RvcmVVc2VyKHVzZXIpLnRoZW4oKCk9PntcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIHRoaXMuX2V2ZW50cy5sb2FkKHVzZXIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHVzZXI7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIG51bGw7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIF92YWxpZGF0ZUlkVG9rZW5Gcm9tVG9rZW5SZWZyZXNoVG9rZW4ocHJvZmlsZSwgaWRfdG9rZW4pIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fbWV0YWRhdGFTZXJ2aWNlLmdldElzc3VlcigpLnRoZW4oaXNzdWVyID0+IHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2pvc2VVdGlsLnZhbGlkYXRlSnd0QXR0cmlidXRlcyhpZF90b2tlbiwgaXNzdWVyLCB0aGlzLl9zZXR0aW5ncy5jbGllbnRfaWQsIHRoaXMuX3NldHRpbmdzLmNsb2NrU2tldykudGhlbihwYXlsb2FkID0+IHtcclxuICAgICAgICAgICAgICAgIGlmICghcGF5bG9hZCkge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5lcnJvcihcIlVzZXJNYW5hZ2VyLl92YWxpZGF0ZUlkVG9rZW5Gcm9tVG9rZW5SZWZyZXNoVG9rZW46IEZhaWxlZCB0byB2YWxpZGF0ZSBpZF90b2tlblwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiRmFpbGVkIHRvIHZhbGlkYXRlIGlkX3Rva2VuXCIpKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGlmIChwYXlsb2FkLnN1YiAhPT0gcHJvZmlsZS5zdWIpIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZXJyb3IoXCJVc2VyTWFuYWdlci5fdmFsaWRhdGVJZFRva2VuRnJvbVRva2VuUmVmcmVzaFRva2VuOiBzdWIgaW4gaWRfdG9rZW4gZG9lcyBub3QgbWF0Y2ggY3VycmVudCBzdWJcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcInN1YiBpbiBpZF90b2tlbiBkb2VzIG5vdCBtYXRjaCBjdXJyZW50IHN1YlwiKSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZiAocGF5bG9hZC5hdXRoX3RpbWUgJiYgcGF5bG9hZC5hdXRoX3RpbWUgIT09IHByb2ZpbGUuYXV0aF90aW1lKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiVXNlck1hbmFnZXIuX3ZhbGlkYXRlSWRUb2tlbkZyb21Ub2tlblJlZnJlc2hUb2tlbjogYXV0aF90aW1lIGluIGlkX3Rva2VuIGRvZXMgbm90IG1hdGNoIG9yaWdpbmFsIGF1dGhfdGltZVwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiYXV0aF90aW1lIGluIGlkX3Rva2VuIGRvZXMgbm90IG1hdGNoIG9yaWdpbmFsIGF1dGhfdGltZVwiKSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZiAocGF5bG9hZC5henAgJiYgcGF5bG9hZC5henAgIT09IHByb2ZpbGUuYXpwKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiVXNlck1hbmFnZXIuX3ZhbGlkYXRlSWRUb2tlbkZyb21Ub2tlblJlZnJlc2hUb2tlbjogYXpwIGluIGlkX3Rva2VuIGRvZXMgbm90IG1hdGNoIG9yaWdpbmFsIGF6cFwiKTtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gUHJvbWlzZS5yZWplY3QobmV3IEVycm9yKFwiYXpwIGluIGlkX3Rva2VuIGRvZXMgbm90IG1hdGNoIG9yaWdpbmFsIGF6cFwiKSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZiAoIXBheWxvYWQuYXpwICYmIHByb2ZpbGUuYXpwKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmVycm9yKFwiVXNlck1hbmFnZXIuX3ZhbGlkYXRlSWRUb2tlbkZyb21Ub2tlblJlZnJlc2hUb2tlbjogYXpwIG5vdCBpbiBpZF90b2tlbiwgYnV0IHByZXNlbnQgaW4gb3JpZ2luYWwgaWRfdG9rZW5cIik7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcImF6cCBub3QgaW4gaWRfdG9rZW4sIGJ1dCBwcmVzZW50IGluIG9yaWdpbmFsIGlkX3Rva2VuXCIpKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBcclxuICAgIF9zaWduaW5TaWxlbnRJZnJhbWUoYXJncyA9IHt9KSB7XHJcbiAgICAgICAgbGV0IHVybCA9IGFyZ3MucmVkaXJlY3RfdXJpIHx8IHRoaXMuc2V0dGluZ3Muc2lsZW50X3JlZGlyZWN0X3VyaSB8fCB0aGlzLnNldHRpbmdzLnJlZGlyZWN0X3VyaTtcclxuICAgICAgICBpZiAoIXVybCkge1xyXG4gICAgICAgICAgICBMb2cuZXJyb3IoXCJVc2VyTWFuYWdlci5zaWduaW5TaWxlbnQ6IE5vIHNpbGVudF9yZWRpcmVjdF91cmkgY29uZmlndXJlZFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcIk5vIHNpbGVudF9yZWRpcmVjdF91cmkgY29uZmlndXJlZFwiKSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICBhcmdzLnJlZGlyZWN0X3VyaSA9IHVybDtcclxuICAgICAgICBhcmdzLnByb21wdCA9IGFyZ3MucHJvbXB0IHx8IFwibm9uZVwiO1xyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fc2lnbmluKGFyZ3MsIHRoaXMuX2lmcmFtZU5hdmlnYXRvciwge1xyXG4gICAgICAgICAgICBzdGFydFVybDogdXJsLFxyXG4gICAgICAgICAgICBzaWxlbnRSZXF1ZXN0VGltZW91dDogYXJncy5zaWxlbnRSZXF1ZXN0VGltZW91dCB8fCB0aGlzLnNldHRpbmdzLnNpbGVudFJlcXVlc3RUaW1lb3V0XHJcbiAgICAgICAgfSkudGhlbih1c2VyID0+IHtcclxuICAgICAgICAgICAgaWYgKHVzZXIpIHtcclxuICAgICAgICAgICAgICAgIGlmICh1c2VyLnByb2ZpbGUgJiYgdXNlci5wcm9maWxlLnN1Yikge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5pbmZvKFwiVXNlck1hbmFnZXIuc2lnbmluU2lsZW50OiBzdWNjZXNzZnVsLCBzaWduZWQgaW4gc3ViOiBcIiwgdXNlci5wcm9maWxlLnN1Yik7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnNpZ25pblNpbGVudDogbm8gc3ViXCIpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gdXNlcjtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBzaWduaW5TaWxlbnRDYWxsYmFjayh1cmwpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc2lnbmluQ2FsbGJhY2sodXJsLCB0aGlzLl9pZnJhbWVOYXZpZ2F0b3IpLnRoZW4odXNlciA9PiB7XHJcbiAgICAgICAgICAgIGlmICh1c2VyKSB7XHJcbiAgICAgICAgICAgICAgICBpZiAodXNlci5wcm9maWxlICYmIHVzZXIucHJvZmlsZS5zdWIpIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnNpZ25pblNpbGVudENhbGxiYWNrOiBzdWNjZXNzZnVsLCBzaWduZWQgaW4gc3ViOiBcIiwgdXNlci5wcm9maWxlLnN1Yik7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBlbHNlIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnNpZ25pblNpbGVudENhbGxiYWNrOiBubyBzdWJcIik7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHJldHVybiB1c2VyO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHNpZ25pbkNhbGxiYWNrKHVybCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnJlYWRTaWduaW5SZXNwb25zZVN0YXRlKHVybCkudGhlbigoe3N0YXRlLCByZXNwb25zZX0pID0+IHtcclxuICAgICAgICAgICAgaWYgKHN0YXRlLnJlcXVlc3RfdHlwZSA9PT0gXCJzaTpyXCIpIHtcclxuICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnNpZ25pblJlZGlyZWN0Q2FsbGJhY2sodXJsKTtcclxuICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICBpZiAoc3RhdGUucmVxdWVzdF90eXBlID09PSBcInNpOnBcIikge1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMuc2lnbmluUG9wdXBDYWxsYmFjayh1cmwpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIGlmIChzdGF0ZS5yZXF1ZXN0X3R5cGUgPT09IFwic2k6c1wiKSB7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zaWduaW5TaWxlbnRDYWxsYmFjayh1cmwpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJpbnZhbGlkIHJlc3BvbnNlX3R5cGUgaW4gc3RhdGVcIikpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHNpZ25vdXRDYWxsYmFjayh1cmwsIGtlZXBPcGVuKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMucmVhZFNpZ25vdXRSZXNwb25zZVN0YXRlKHVybCkudGhlbigoe3N0YXRlLCByZXNwb25zZX0pID0+IHtcclxuICAgICAgICAgICAgaWYgKHN0YXRlKSB7XHJcbiAgICAgICAgICAgICAgICBpZiAoc3RhdGUucmVxdWVzdF90eXBlID09PSBcInNvOnJcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnNpZ25vdXRSZWRpcmVjdENhbGxiYWNrKHVybCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICBpZiAoc3RhdGUucmVxdWVzdF90eXBlID09PSBcInNvOnBcIikge1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLnNpZ25vdXRQb3B1cENhbGxiYWNrKHVybCwga2VlcE9wZW4pO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcImludmFsaWQgcmVzcG9uc2VfdHlwZSBpbiBzdGF0ZVwiKSk7XHJcbiAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHF1ZXJ5U2Vzc2lvblN0YXR1cyhhcmdzID0ge30pIHtcclxuICAgICAgICBhcmdzID0gT2JqZWN0LmFzc2lnbih7fSwgYXJncyk7XHJcblxyXG4gICAgICAgIGFyZ3MucmVxdWVzdF90eXBlID0gXCJzaTpzXCI7IC8vIHRoaXMgYWN0cyBsaWtlIGEgc2lnbmluIHNpbGVudFxyXG4gICAgICAgIGxldCB1cmwgPSBhcmdzLnJlZGlyZWN0X3VyaSB8fCB0aGlzLnNldHRpbmdzLnNpbGVudF9yZWRpcmVjdF91cmkgfHwgdGhpcy5zZXR0aW5ncy5yZWRpcmVjdF91cmk7XHJcbiAgICAgICAgaWYgKCF1cmwpIHtcclxuICAgICAgICAgICAgTG9nLmVycm9yKFwiVXNlck1hbmFnZXIucXVlcnlTZXNzaW9uU3RhdHVzOiBObyBzaWxlbnRfcmVkaXJlY3RfdXJpIGNvbmZpZ3VyZWRcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlamVjdChuZXcgRXJyb3IoXCJObyBzaWxlbnRfcmVkaXJlY3RfdXJpIGNvbmZpZ3VyZWRcIikpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgYXJncy5yZWRpcmVjdF91cmkgPSB1cmw7XHJcbiAgICAgICAgYXJncy5wcm9tcHQgPSBcIm5vbmVcIjtcclxuICAgICAgICBhcmdzLnJlc3BvbnNlX3R5cGUgPSBhcmdzLnJlc3BvbnNlX3R5cGUgfHwgdGhpcy5zZXR0aW5ncy5xdWVyeV9zdGF0dXNfcmVzcG9uc2VfdHlwZTtcclxuICAgICAgICBhcmdzLnNjb3BlID0gYXJncy5zY29wZSB8fCBcIm9wZW5pZFwiO1xyXG4gICAgICAgIGFyZ3Muc2tpcFVzZXJJbmZvID0gdHJ1ZTtcclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3NpZ25pblN0YXJ0KGFyZ3MsIHRoaXMuX2lmcmFtZU5hdmlnYXRvciwge1xyXG4gICAgICAgICAgICBzdGFydFVybDogdXJsLFxyXG4gICAgICAgICAgICBzaWxlbnRSZXF1ZXN0VGltZW91dDogYXJncy5zaWxlbnRSZXF1ZXN0VGltZW91dCB8fCB0aGlzLnNldHRpbmdzLnNpbGVudFJlcXVlc3RUaW1lb3V0XHJcbiAgICAgICAgfSkudGhlbihuYXZSZXNwb25zZSA9PiB7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnByb2Nlc3NTaWduaW5SZXNwb25zZShuYXZSZXNwb25zZS51cmwpLnRoZW4oc2lnbmluUmVzcG9uc2UgPT4ge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIucXVlcnlTZXNzaW9uU3RhdHVzOiBnb3Qgc2lnbmluIHJlc3BvbnNlXCIpO1xyXG5cclxuICAgICAgICAgICAgICAgIGlmIChzaWduaW5SZXNwb25zZS5zZXNzaW9uX3N0YXRlICYmIHNpZ25pblJlc3BvbnNlLnByb2ZpbGUuc3ViKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmluZm8oXCJVc2VyTWFuYWdlci5xdWVyeVNlc3Npb25TdGF0dXM6IHF1ZXJ5U2Vzc2lvblN0YXR1cyBzdWNjZXNzIGZvciBzdWI6IFwiLCAgc2lnbmluUmVzcG9uc2UucHJvZmlsZS5zdWIpO1xyXG4gICAgICAgICAgICAgICAgICAgIHJldHVybiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNlc3Npb25fc3RhdGU6IHNpZ25pblJlc3BvbnNlLnNlc3Npb25fc3RhdGUsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHN1Yjogc2lnbmluUmVzcG9uc2UucHJvZmlsZS5zdWIsXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHNpZDogc2lnbmluUmVzcG9uc2UucHJvZmlsZS5zaWRcclxuICAgICAgICAgICAgICAgICAgICB9O1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmluZm8oXCJxdWVyeVNlc3Npb25TdGF0dXMgc3VjY2Vzc2Z1bCwgdXNlciBub3QgYXV0aGVudGljYXRlZFwiKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgfSlcclxuICAgICAgICAgICAgLmNhdGNoKGVyciA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZiAoZXJyLnNlc3Npb25fc3RhdGUgJiYgdGhpcy5zZXR0aW5ncy5tb25pdG9yQW5vbnltb3VzU2Vzc2lvbikge1xyXG4gICAgICAgICAgICAgICAgICAgIGlmIChlcnIubWVzc2FnZSA9PSBcImxvZ2luX3JlcXVpcmVkXCIgfHwgXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGVyci5tZXNzYWdlID09IFwiY29uc2VudF9yZXF1aXJlZFwiIHx8IFxyXG4gICAgICAgICAgICAgICAgICAgICAgICBlcnIubWVzc2FnZSA9PSBcImludGVyYWN0aW9uX3JlcXVpcmVkXCIgfHwgXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGVyci5tZXNzYWdlID09IFwiYWNjb3VudF9zZWxlY3Rpb25fcmVxdWlyZWRcIlxyXG4gICAgICAgICAgICAgICAgICAgICkge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnF1ZXJ5U2Vzc2lvblN0YXR1czogcXVlcnlTZXNzaW9uU3RhdHVzIHN1Y2Nlc3MgZm9yIGFub255bW91cyB1c2VyXCIpO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgc2Vzc2lvbl9zdGF0ZTogZXJyLnNlc3Npb25fc3RhdGVcclxuICAgICAgICAgICAgICAgICAgICAgICAgfTtcclxuICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB9XHJcblxyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBfc2lnbmluKGFyZ3MsIG5hdmlnYXRvciwgbmF2aWdhdG9yUGFyYW1zID0ge30pIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc2lnbmluU3RhcnQoYXJncywgbmF2aWdhdG9yLCBuYXZpZ2F0b3JQYXJhbXMpLnRoZW4obmF2UmVzcG9uc2UgPT4ge1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fc2lnbmluRW5kKG5hdlJlc3BvbnNlLnVybCwgYXJncyk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBfc2lnbmluU3RhcnQoYXJncywgbmF2aWdhdG9yLCBuYXZpZ2F0b3JQYXJhbXMgPSB7fSkge1xyXG5cclxuICAgICAgICByZXR1cm4gbmF2aWdhdG9yLnByZXBhcmUobmF2aWdhdG9yUGFyYW1zKS50aGVuKGhhbmRsZSA9PiB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLl9zaWduaW5TdGFydDogZ290IG5hdmlnYXRvciB3aW5kb3cgaGFuZGxlXCIpO1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuY3JlYXRlU2lnbmluUmVxdWVzdChhcmdzKS50aGVuKHNpZ25pblJlcXVlc3QgPT4ge1xyXG4gICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIuX3NpZ25pblN0YXJ0OiBnb3Qgc2lnbmluIHJlcXVlc3RcIik7XHJcblxyXG4gICAgICAgICAgICAgICAgbmF2aWdhdG9yUGFyYW1zLnVybCA9IHNpZ25pblJlcXVlc3QudXJsO1xyXG4gICAgICAgICAgICAgICAgbmF2aWdhdG9yUGFyYW1zLmlkID0gc2lnbmluUmVxdWVzdC5zdGF0ZS5pZDtcclxuXHJcbiAgICAgICAgICAgICAgICByZXR1cm4gaGFuZGxlLm5hdmlnYXRlKG5hdmlnYXRvclBhcmFtcyk7XHJcbiAgICAgICAgICAgIH0pLmNhdGNoKGVyciA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZiAoaGFuZGxlLmNsb3NlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIuX3NpZ25pblN0YXJ0OiBFcnJvciBhZnRlciBwcmVwYXJpbmcgbmF2aWdhdG9yLCBjbG9zaW5nIG5hdmlnYXRvciB3aW5kb3dcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgaGFuZGxlLmNsb3NlKCk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICB0aHJvdyBlcnI7XHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgX3NpZ25pbkVuZCh1cmwsIGFyZ3MgPSB7fSkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnByb2Nlc3NTaWduaW5SZXNwb25zZSh1cmwpLnRoZW4oc2lnbmluUmVzcG9uc2UgPT4ge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VyTWFuYWdlci5fc2lnbmluRW5kOiBnb3Qgc2lnbmluIHJlc3BvbnNlXCIpO1xyXG5cclxuICAgICAgICAgICAgbGV0IHVzZXIgPSBuZXcgVXNlcihzaWduaW5SZXNwb25zZSk7XHJcblxyXG4gICAgICAgICAgICBpZiAoYXJncy5jdXJyZW50X3N1Yikge1xyXG4gICAgICAgICAgICAgICAgaWYgKGFyZ3MuY3VycmVudF9zdWIgIT09IHVzZXIucHJvZmlsZS5zdWIpIHtcclxuICAgICAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VyTWFuYWdlci5fc2lnbmluRW5kOiBjdXJyZW50IHVzZXIgZG9lcyBub3QgbWF0Y2ggdXNlciByZXR1cm5lZCBmcm9tIHNpZ25pbi4gc3ViIGZyb20gc2lnbmluOiBcIiwgdXNlci5wcm9maWxlLnN1Yik7XHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVqZWN0KG5ldyBFcnJvcihcImxvZ2luX3JlcXVpcmVkXCIpKTtcclxuICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLl9zaWduaW5FbmQ6IGN1cnJlbnQgdXNlciBtYXRjaGVzIHVzZXIgcmV0dXJuZWQgZnJvbSBzaWduaW5cIik7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLnN0b3JlVXNlcih1c2VyKS50aGVuKCgpID0+IHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLl9zaWduaW5FbmQ6IHVzZXIgc3RvcmVkXCIpO1xyXG5cclxuICAgICAgICAgICAgICAgIHRoaXMuX2V2ZW50cy5sb2FkKHVzZXIpO1xyXG5cclxuICAgICAgICAgICAgICAgIHJldHVybiB1c2VyO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuICAgIF9zaWduaW5DYWxsYmFjayh1cmwsIG5hdmlnYXRvcikge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLl9zaWduaW5DYWxsYmFja1wiKTtcclxuICAgICAgICByZXR1cm4gbmF2aWdhdG9yLmNhbGxiYWNrKHVybCk7XHJcbiAgICB9XHJcblxyXG4gICAgc2lnbm91dFJlZGlyZWN0KGFyZ3MgPSB7fSkge1xyXG4gICAgICAgIGFyZ3MgPSBPYmplY3QuYXNzaWduKHt9LCBhcmdzKTtcclxuXHJcbiAgICAgICAgYXJncy5yZXF1ZXN0X3R5cGUgPSBcInNvOnJcIjtcclxuICAgICAgICBsZXQgcG9zdExvZ291dFJlZGlyZWN0VXJpID0gYXJncy5wb3N0X2xvZ291dF9yZWRpcmVjdF91cmkgfHwgdGhpcy5zZXR0aW5ncy5wb3N0X2xvZ291dF9yZWRpcmVjdF91cmk7XHJcbiAgICAgICAgaWYgKHBvc3RMb2dvdXRSZWRpcmVjdFVyaSl7XHJcbiAgICAgICAgICAgIGFyZ3MucG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpID0gcG9zdExvZ291dFJlZGlyZWN0VXJpO1xyXG4gICAgICAgIH1cclxuICAgICAgICBsZXQgbmF2UGFyYW1zID0ge1xyXG4gICAgICAgICAgICB1c2VSZXBsYWNlVG9OYXZpZ2F0ZSA6IGFyZ3MudXNlUmVwbGFjZVRvTmF2aWdhdGVcclxuICAgICAgICB9O1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9zaWdub3V0U3RhcnQoYXJncywgdGhpcy5fcmVkaXJlY3ROYXZpZ2F0b3IsIG5hdlBhcmFtcykudGhlbigoKT0+e1xyXG4gICAgICAgICAgICBMb2cuaW5mbyhcIlVzZXJNYW5hZ2VyLnNpZ25vdXRSZWRpcmVjdDogc3VjY2Vzc2Z1bFwiKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuICAgIHNpZ25vdXRSZWRpcmVjdENhbGxiYWNrKHVybCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9zaWdub3V0RW5kKHVybCB8fCB0aGlzLl9yZWRpcmVjdE5hdmlnYXRvci51cmwpLnRoZW4ocmVzcG9uc2U9PntcclxuICAgICAgICAgICAgTG9nLmluZm8oXCJVc2VyTWFuYWdlci5zaWdub3V0UmVkaXJlY3RDYWxsYmFjazogc3VjY2Vzc2Z1bFwiKTtcclxuICAgICAgICAgICAgcmV0dXJuIHJlc3BvbnNlO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHNpZ25vdXRQb3B1cChhcmdzID0ge30pIHtcclxuICAgICAgICBhcmdzID0gT2JqZWN0LmFzc2lnbih7fSwgYXJncyk7XHJcblxyXG4gICAgICAgIGFyZ3MucmVxdWVzdF90eXBlID0gXCJzbzpwXCI7XHJcbiAgICAgICAgbGV0IHVybCA9IGFyZ3MucG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpIHx8IHRoaXMuc2V0dGluZ3MucG9wdXBfcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpIHx8IHRoaXMuc2V0dGluZ3MucG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpO1xyXG4gICAgICAgIGFyZ3MucG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpID0gdXJsO1xyXG4gICAgICAgIGFyZ3MuZGlzcGxheSA9IFwicG9wdXBcIjtcclxuICAgICAgICBpZiAoYXJncy5wb3N0X2xvZ291dF9yZWRpcmVjdF91cmkpe1xyXG4gICAgICAgICAgICAvLyB3ZSdyZSBwdXR0aW5nIGEgZHVtbXkgZW50cnkgaW4gaGVyZSBiZWNhdXNlIHdlXHJcbiAgICAgICAgICAgIC8vIG5lZWQgYSB1bmlxdWUgaWQgZnJvbSB0aGUgc3RhdGUgZm9yIG5vdGlmaWNhdGlvblxyXG4gICAgICAgICAgICAvLyB0byB0aGUgcGFyZW50IHdpbmRvdywgd2hpY2ggaXMgbmVjZXNzYXJ5IGlmIHdlXHJcbiAgICAgICAgICAgIC8vIHBsYW4gdG8gcmV0dXJuIGJhY2sgdG8gdGhlIGNsaWVudCBhZnRlciBzaWdub3V0XHJcbiAgICAgICAgICAgIC8vIGFuZCBzbyB3ZSBjYW4gY2xvc2UgdGhlIHBvcHVwIGFmdGVyIHNpZ25vdXRcclxuICAgICAgICAgICAgYXJncy5zdGF0ZSA9IGFyZ3Muc3RhdGUgfHwge307XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fc2lnbm91dChhcmdzLCB0aGlzLl9wb3B1cE5hdmlnYXRvciwge1xyXG4gICAgICAgICAgICBzdGFydFVybDogdXJsLFxyXG4gICAgICAgICAgICBwb3B1cFdpbmRvd0ZlYXR1cmVzOiBhcmdzLnBvcHVwV2luZG93RmVhdHVyZXMgfHwgdGhpcy5zZXR0aW5ncy5wb3B1cFdpbmRvd0ZlYXR1cmVzLFxyXG4gICAgICAgICAgICBwb3B1cFdpbmRvd1RhcmdldDogYXJncy5wb3B1cFdpbmRvd1RhcmdldCB8fCB0aGlzLnNldHRpbmdzLnBvcHVwV2luZG93VGFyZ2V0XHJcbiAgICAgICAgfSkudGhlbigoKSA9PiB7XHJcbiAgICAgICAgICAgIExvZy5pbmZvKFwiVXNlck1hbmFnZXIuc2lnbm91dFBvcHVwOiBzdWNjZXNzZnVsXCIpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG4gICAgc2lnbm91dFBvcHVwQ2FsbGJhY2sodXJsLCBrZWVwT3Blbikge1xyXG4gICAgICAgIGlmICh0eXBlb2Yoa2VlcE9wZW4pID09PSAndW5kZWZpbmVkJyAmJiB0eXBlb2YodXJsKSA9PT0gJ2Jvb2xlYW4nKSB7XHJcbiAgICAgICAgICAgIGtlZXBPcGVuID0gdXJsO1xyXG4gICAgICAgICAgICB1cmwgPSBudWxsO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgbGV0IGRlbGltaXRlciA9ICc/JztcclxuICAgICAgICByZXR1cm4gdGhpcy5fcG9wdXBOYXZpZ2F0b3IuY2FsbGJhY2sodXJsLCBrZWVwT3BlbiwgZGVsaW1pdGVyKS50aGVuKCgpID0+IHtcclxuICAgICAgICAgICAgTG9nLmluZm8oXCJVc2VyTWFuYWdlci5zaWdub3V0UG9wdXBDYWxsYmFjazogc3VjY2Vzc2Z1bFwiKTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuXHJcbiAgICBfc2lnbm91dChhcmdzLCBuYXZpZ2F0b3IsIG5hdmlnYXRvclBhcmFtcyA9IHt9KSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3NpZ25vdXRTdGFydChhcmdzLCBuYXZpZ2F0b3IsIG5hdmlnYXRvclBhcmFtcykudGhlbihuYXZSZXNwb25zZSA9PiB7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLl9zaWdub3V0RW5kKG5hdlJlc3BvbnNlLnVybCk7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcbiAgICBfc2lnbm91dFN0YXJ0KGFyZ3MgPSB7fSwgbmF2aWdhdG9yLCBuYXZpZ2F0b3JQYXJhbXMgPSB7fSkge1xyXG4gICAgICAgIHJldHVybiBuYXZpZ2F0b3IucHJlcGFyZShuYXZpZ2F0b3JQYXJhbXMpLnRoZW4oaGFuZGxlID0+IHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIuX3NpZ25vdXRTdGFydDogZ290IG5hdmlnYXRvciB3aW5kb3cgaGFuZGxlXCIpO1xyXG5cclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX2xvYWRVc2VyKCkudGhlbih1c2VyID0+IHtcclxuICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLl9zaWdub3V0U3RhcnQ6IGxvYWRlZCBjdXJyZW50IHVzZXIgZnJvbSBzdG9yYWdlXCIpO1xyXG5cclxuICAgICAgICAgICAgICAgIHZhciByZXZva2VQcm9taXNlID0gdGhpcy5fc2V0dGluZ3MucmV2b2tlQWNjZXNzVG9rZW5PblNpZ25vdXQgPyB0aGlzLl9yZXZva2VJbnRlcm5hbCh1c2VyKSA6IFByb21pc2UucmVzb2x2ZSgpO1xyXG4gICAgICAgICAgICAgICAgcmV0dXJuIHJldm9rZVByb21pc2UudGhlbigoKSA9PiB7XHJcblxyXG4gICAgICAgICAgICAgICAgICAgIHZhciBpZF90b2tlbiA9IGFyZ3MuaWRfdG9rZW5faGludCB8fCB1c2VyICYmIHVzZXIuaWRfdG9rZW47XHJcbiAgICAgICAgICAgICAgICAgICAgaWYgKGlkX3Rva2VuKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLl9zaWdub3V0U3RhcnQ6IFNldHRpbmcgaWRfdG9rZW4gaW50byBzaWdub3V0IHJlcXVlc3RcIik7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIGFyZ3MuaWRfdG9rZW5faGludCA9IGlkX3Rva2VuO1xyXG4gICAgICAgICAgICAgICAgICAgIH1cclxuXHJcbiAgICAgICAgICAgICAgICAgICAgcmV0dXJuIHRoaXMucmVtb3ZlVXNlcigpLnRoZW4oKCkgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VyTWFuYWdlci5fc2lnbm91dFN0YXJ0OiB1c2VyIHJlbW92ZWQsIGNyZWF0aW5nIHNpZ25vdXQgcmVxdWVzdFwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIHJldHVybiB0aGlzLmNyZWF0ZVNpZ25vdXRSZXF1ZXN0KGFyZ3MpLnRoZW4oc2lnbm91dFJlcXVlc3QgPT4ge1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIuX3NpZ25vdXRTdGFydDogZ290IHNpZ25vdXQgcmVxdWVzdFwiKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBuYXZpZ2F0b3JQYXJhbXMudXJsID0gc2lnbm91dFJlcXVlc3QudXJsO1xyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgaWYgKHNpZ25vdXRSZXF1ZXN0LnN0YXRlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgbmF2aWdhdG9yUGFyYW1zLmlkID0gc2lnbm91dFJlcXVlc3Quc3RhdGUuaWQ7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICByZXR1cm4gaGFuZGxlLm5hdmlnYXRlKG5hdmlnYXRvclBhcmFtcyk7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgIH0pLmNhdGNoKGVyciA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZiAoaGFuZGxlLmNsb3NlKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIuX3NpZ25vdXRTdGFydDogRXJyb3IgYWZ0ZXIgcHJlcGFyaW5nIG5hdmlnYXRvciwgY2xvc2luZyBuYXZpZ2F0b3Igd2luZG93XCIpO1xyXG4gICAgICAgICAgICAgICAgICAgIGhhbmRsZS5jbG9zZSgpO1xyXG4gICAgICAgICAgICAgICAgfVxyXG4gICAgICAgICAgICAgICAgdGhyb3cgZXJyO1xyXG4gICAgICAgICAgICB9KTtcclxuICAgICAgICB9KTtcclxuICAgIH1cclxuICAgIF9zaWdub3V0RW5kKHVybCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLnByb2Nlc3NTaWdub3V0UmVzcG9uc2UodXJsKS50aGVuKHNpZ25vdXRSZXNwb25zZSA9PiB7XHJcbiAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLl9zaWdub3V0RW5kOiBnb3Qgc2lnbm91dCByZXNwb25zZVwiKTtcclxuXHJcbiAgICAgICAgICAgIHJldHVybiBzaWdub3V0UmVzcG9uc2U7XHJcbiAgICAgICAgfSk7XHJcbiAgICB9XHJcblxyXG4gICAgcmV2b2tlQWNjZXNzVG9rZW4oKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2xvYWRVc2VyKCkudGhlbih1c2VyID0+IHtcclxuICAgICAgICAgICAgcmV0dXJuIHRoaXMuX3Jldm9rZUludGVybmFsKHVzZXIsIHRydWUpLnRoZW4oc3VjY2VzcyA9PiB7XHJcbiAgICAgICAgICAgICAgICBpZiAoc3VjY2Vzcykge1xyXG4gICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLnJldm9rZUFjY2Vzc1Rva2VuOiByZW1vdmluZyB0b2tlbiBwcm9wZXJ0aWVzIGZyb20gdXNlciBhbmQgcmUtc3RvcmluZ1wiKTtcclxuXHJcbiAgICAgICAgICAgICAgICAgICAgdXNlci5hY2Nlc3NfdG9rZW4gPSBudWxsO1xyXG4gICAgICAgICAgICAgICAgICAgIHVzZXIucmVmcmVzaF90b2tlbiA9IG51bGw7XHJcbiAgICAgICAgICAgICAgICAgICAgdXNlci5leHBpcmVzX2F0ID0gbnVsbDtcclxuICAgICAgICAgICAgICAgICAgICB1c2VyLnRva2VuX3R5cGUgPSBudWxsO1xyXG5cclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5zdG9yZVVzZXIodXNlcikudGhlbigoKSA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyLnJldm9rZUFjY2Vzc1Rva2VuOiB1c2VyIHN0b3JlZFwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgdGhpcy5fZXZlbnRzLmxvYWQodXNlcik7XHJcbiAgICAgICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgICAgICAgICB9XHJcbiAgICAgICAgICAgIH0pO1xyXG4gICAgICAgIH0pLnRoZW4oKCk9PntcclxuICAgICAgICAgICAgTG9nLmluZm8oXCJVc2VyTWFuYWdlci5yZXZva2VBY2Nlc3NUb2tlbjogYWNjZXNzIHRva2VuIHJldm9rZWQgc3VjY2Vzc2Z1bGx5XCIpO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIF9yZXZva2VJbnRlcm5hbCh1c2VyLCByZXF1aXJlZCkge1xyXG4gICAgICAgIGlmICh1c2VyKSB7XHJcbiAgICAgICAgICAgIHZhciBhY2Nlc3NfdG9rZW4gPSB1c2VyLmFjY2Vzc190b2tlbjtcclxuICAgICAgICAgICAgdmFyIHJlZnJlc2hfdG9rZW4gPSB1c2VyLnJlZnJlc2hfdG9rZW47XHJcblxyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fcmV2b2tlQWNjZXNzVG9rZW5JbnRlcm5hbChhY2Nlc3NfdG9rZW4sIHJlcXVpcmVkKVxyXG4gICAgICAgICAgICAgICAgLnRoZW4oYXRTdWNjZXNzID0+IHtcclxuICAgICAgICAgICAgICAgICAgICByZXR1cm4gdGhpcy5fcmV2b2tlUmVmcmVzaFRva2VuSW50ZXJuYWwocmVmcmVzaF90b2tlbiwgcmVxdWlyZWQpXHJcbiAgICAgICAgICAgICAgICAgICAgICAgIC50aGVuKHJ0U3VjY2VzcyA9PiB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICBpZiAoIWF0U3VjY2VzcyAmJiAhcnRTdWNjZXNzKSB7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIucmV2b2tlQWNjZXNzVG9rZW46IG5vIG5lZWQgdG8gcmV2b2tlIGR1ZSB0byBubyB0b2tlbihzKSwgb3IgSldUIGZvcm1hdFwiKTtcclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIH1cclxuICAgICAgICAgICAgICAgICAgICAgICAgICAgIFxyXG4gICAgICAgICAgICAgICAgICAgICAgICAgICAgcmV0dXJuIGF0U3VjY2VzcyB8fCBydFN1Y2Nlc3M7XHJcbiAgICAgICAgICAgICAgICAgICAgICAgIH0pO1xyXG4gICAgICAgICAgICAgICAgfSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGZhbHNlKTtcclxuICAgIH1cclxuXHJcbiAgICBfcmV2b2tlQWNjZXNzVG9rZW5JbnRlcm5hbChhY2Nlc3NfdG9rZW4sIHJlcXVpcmVkKSB7XHJcbiAgICAgICAgLy8gY2hlY2sgZm9yIEpXVCB2cy4gcmVmZXJlbmNlIHRva2VuXHJcbiAgICAgICAgaWYgKCFhY2Nlc3NfdG9rZW4gfHwgYWNjZXNzX3Rva2VuLmluZGV4T2YoJy4nKSA+PSAwKSB7XHJcbiAgICAgICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoZmFsc2UpO1xyXG4gICAgICAgIH1cclxuXHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Rva2VuUmV2b2NhdGlvbkNsaWVudC5yZXZva2UoYWNjZXNzX3Rva2VuLCByZXF1aXJlZCkudGhlbigoKSA9PiB0cnVlKTtcclxuICAgIH1cclxuXHJcbiAgICBfcmV2b2tlUmVmcmVzaFRva2VuSW50ZXJuYWwocmVmcmVzaF90b2tlbiwgcmVxdWlyZWQpIHtcclxuICAgICAgICBpZiAoIXJlZnJlc2hfdG9rZW4pIHtcclxuICAgICAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShmYWxzZSk7XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gdGhpcy5fdG9rZW5SZXZvY2F0aW9uQ2xpZW50LnJldm9rZShyZWZyZXNoX3Rva2VuLCByZXF1aXJlZCwgXCJyZWZyZXNoX3Rva2VuXCIpLnRoZW4oKCkgPT4gdHJ1ZSk7XHJcbiAgICB9XHJcblxyXG4gICAgc3RhcnRTaWxlbnRSZW5ldygpIHtcclxuICAgICAgICB0aGlzLl9zaWxlbnRSZW5ld1NlcnZpY2Uuc3RhcnQoKTtcclxuICAgIH1cclxuXHJcbiAgICBzdG9wU2lsZW50UmVuZXcoKSB7XHJcbiAgICAgICAgdGhpcy5fc2lsZW50UmVuZXdTZXJ2aWNlLnN0b3AoKTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgX3VzZXJTdG9yZUtleSgpIHtcclxuICAgICAgICByZXR1cm4gYHVzZXI6JHt0aGlzLnNldHRpbmdzLmF1dGhvcml0eX06JHt0aGlzLnNldHRpbmdzLmNsaWVudF9pZH1gO1xyXG4gICAgfVxyXG5cclxuICAgIF9sb2FkVXNlcigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fdXNlclN0b3JlLmdldCh0aGlzLl91c2VyU3RvcmVLZXkpLnRoZW4oc3RvcmFnZVN0cmluZyA9PiB7XHJcbiAgICAgICAgICAgIGlmIChzdG9yYWdlU3RyaW5nKSB7XHJcbiAgICAgICAgICAgICAgICBMb2cuZGVidWcoXCJVc2VyTWFuYWdlci5fbG9hZFVzZXI6IHVzZXIgc3RvcmFnZVN0cmluZyBsb2FkZWRcIik7XHJcbiAgICAgICAgICAgICAgICByZXR1cm4gVXNlci5mcm9tU3RvcmFnZVN0cmluZyhzdG9yYWdlU3RyaW5nKTtcclxuICAgICAgICAgICAgfVxyXG5cclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIuX2xvYWRVc2VyOiBubyB1c2VyIHN0b3JhZ2VTdHJpbmdcIik7XHJcbiAgICAgICAgICAgIHJldHVybiBudWxsO1xyXG4gICAgICAgIH0pO1xyXG4gICAgfVxyXG5cclxuICAgIHN0b3JlVXNlcih1c2VyKSB7XHJcbiAgICAgICAgaWYgKHVzZXIpIHtcclxuICAgICAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXIuc3RvcmVVc2VyOiBzdG9yaW5nIHVzZXJcIik7XHJcblxyXG4gICAgICAgICAgICB2YXIgc3RvcmFnZVN0cmluZyA9IHVzZXIudG9TdG9yYWdlU3RyaW5nKCk7XHJcbiAgICAgICAgICAgIHJldHVybiB0aGlzLl91c2VyU3RvcmUuc2V0KHRoaXMuX3VzZXJTdG9yZUtleSwgc3RvcmFnZVN0cmluZyk7XHJcbiAgICAgICAgfVxyXG4gICAgICAgIGVsc2Uge1xyXG4gICAgICAgICAgICBMb2cuZGVidWcoXCJzdG9yZVVzZXIuc3RvcmVVc2VyOiByZW1vdmluZyB1c2VyXCIpO1xyXG4gICAgICAgICAgICByZXR1cm4gdGhpcy5fdXNlclN0b3JlLnJlbW92ZSh0aGlzLl91c2VyU3RvcmVLZXkpO1xyXG4gICAgICAgIH1cclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcbmltcG9ydCB7IEFjY2Vzc1Rva2VuRXZlbnRzIH0gZnJvbSAnLi9BY2Nlc3NUb2tlbkV2ZW50cy5qcyc7XHJcbmltcG9ydCB7IEV2ZW50IH0gZnJvbSAnLi9FdmVudC5qcyc7XHJcblxyXG5leHBvcnQgY2xhc3MgVXNlck1hbmFnZXJFdmVudHMgZXh0ZW5kcyBBY2Nlc3NUb2tlbkV2ZW50cyB7XHJcblxyXG4gICAgY29uc3RydWN0b3Ioc2V0dGluZ3MpIHtcclxuICAgICAgICBzdXBlcihzZXR0aW5ncyk7XHJcbiAgICAgICAgdGhpcy5fdXNlckxvYWRlZCA9IG5ldyBFdmVudChcIlVzZXIgbG9hZGVkXCIpO1xyXG4gICAgICAgIHRoaXMuX3VzZXJVbmxvYWRlZCA9IG5ldyBFdmVudChcIlVzZXIgdW5sb2FkZWRcIik7XHJcbiAgICAgICAgdGhpcy5fc2lsZW50UmVuZXdFcnJvciA9IG5ldyBFdmVudChcIlNpbGVudCByZW5ldyBlcnJvclwiKTtcclxuICAgICAgICB0aGlzLl91c2VyU2lnbmVkSW4gPSBuZXcgRXZlbnQoXCJVc2VyIHNpZ25lZCBpblwiKTtcclxuICAgICAgICB0aGlzLl91c2VyU2lnbmVkT3V0ID0gbmV3IEV2ZW50KFwiVXNlciBzaWduZWQgb3V0XCIpO1xyXG4gICAgICAgIHRoaXMuX3VzZXJTZXNzaW9uQ2hhbmdlZCA9IG5ldyBFdmVudChcIlVzZXIgc2Vzc2lvbiBjaGFuZ2VkXCIpO1xyXG4gICAgfVxyXG5cclxuICAgIGxvYWQodXNlciwgcmFpc2VFdmVudD10cnVlKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXJFdmVudHMubG9hZFwiKTtcclxuICAgICAgICBzdXBlci5sb2FkKHVzZXIpO1xyXG4gICAgICAgIGlmIChyYWlzZUV2ZW50KSB7XHJcbiAgICAgICAgICAgIHRoaXMuX3VzZXJMb2FkZWQucmFpc2UodXNlcik7XHJcbiAgICAgICAgfVxyXG4gICAgfVxyXG4gICAgdW5sb2FkKCkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyRXZlbnRzLnVubG9hZFwiKTtcclxuICAgICAgICBzdXBlci51bmxvYWQoKTtcclxuICAgICAgICB0aGlzLl91c2VyVW5sb2FkZWQucmFpc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICBhZGRVc2VyTG9hZGVkKGNiKSB7XHJcbiAgICAgICAgdGhpcy5fdXNlckxvYWRlZC5hZGRIYW5kbGVyKGNiKTtcclxuICAgIH1cclxuICAgIHJlbW92ZVVzZXJMb2FkZWQoY2IpIHtcclxuICAgICAgICB0aGlzLl91c2VyTG9hZGVkLnJlbW92ZUhhbmRsZXIoY2IpO1xyXG4gICAgfVxyXG5cclxuICAgIGFkZFVzZXJVbmxvYWRlZChjYikge1xyXG4gICAgICAgIHRoaXMuX3VzZXJVbmxvYWRlZC5hZGRIYW5kbGVyKGNiKTtcclxuICAgIH1cclxuICAgIHJlbW92ZVVzZXJVbmxvYWRlZChjYikge1xyXG4gICAgICAgIHRoaXMuX3VzZXJVbmxvYWRlZC5yZW1vdmVIYW5kbGVyKGNiKTtcclxuICAgIH1cclxuXHJcbiAgICBhZGRTaWxlbnRSZW5ld0Vycm9yKGNiKSB7XHJcbiAgICAgICAgdGhpcy5fc2lsZW50UmVuZXdFcnJvci5hZGRIYW5kbGVyKGNiKTtcclxuICAgIH1cclxuICAgIHJlbW92ZVNpbGVudFJlbmV3RXJyb3IoY2IpIHtcclxuICAgICAgICB0aGlzLl9zaWxlbnRSZW5ld0Vycm9yLnJlbW92ZUhhbmRsZXIoY2IpO1xyXG4gICAgfVxyXG4gICAgX3JhaXNlU2lsZW50UmVuZXdFcnJvcihlKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXJFdmVudHMuX3JhaXNlU2lsZW50UmVuZXdFcnJvclwiLCBlLm1lc3NhZ2UpO1xyXG4gICAgICAgIHRoaXMuX3NpbGVudFJlbmV3RXJyb3IucmFpc2UoZSk7XHJcbiAgICB9XHJcblxyXG4gICAgYWRkVXNlclNpZ25lZEluKGNiKSB7XHJcbiAgICAgICAgdGhpcy5fdXNlclNpZ25lZEluLmFkZEhhbmRsZXIoY2IpO1xyXG4gICAgfVxyXG4gICAgcmVtb3ZlVXNlclNpZ25lZEluKGNiKSB7XHJcbiAgICAgICAgdGhpcy5fdXNlclNpZ25lZEluLnJlbW92ZUhhbmRsZXIoY2IpO1xyXG4gICAgfVxyXG4gICAgX3JhaXNlVXNlclNpZ25lZEluKCkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIlVzZXJNYW5hZ2VyRXZlbnRzLl9yYWlzZVVzZXJTaWduZWRJblwiKTtcclxuICAgICAgICB0aGlzLl91c2VyU2lnbmVkSW4ucmFpc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICBhZGRVc2VyU2lnbmVkT3V0KGNiKSB7XHJcbiAgICAgICAgdGhpcy5fdXNlclNpZ25lZE91dC5hZGRIYW5kbGVyKGNiKTtcclxuICAgIH1cclxuICAgIHJlbW92ZVVzZXJTaWduZWRPdXQoY2IpIHtcclxuICAgICAgICB0aGlzLl91c2VyU2lnbmVkT3V0LnJlbW92ZUhhbmRsZXIoY2IpO1xyXG4gICAgfVxyXG4gICAgX3JhaXNlVXNlclNpZ25lZE91dCgpIHtcclxuICAgICAgICBMb2cuZGVidWcoXCJVc2VyTWFuYWdlckV2ZW50cy5fcmFpc2VVc2VyU2lnbmVkT3V0XCIpO1xyXG4gICAgICAgIHRoaXMuX3VzZXJTaWduZWRPdXQucmFpc2UoKTtcclxuICAgIH1cclxuXHJcbiAgICBhZGRVc2VyU2Vzc2lvbkNoYW5nZWQoY2IpIHtcclxuICAgICAgICB0aGlzLl91c2VyU2Vzc2lvbkNoYW5nZWQuYWRkSGFuZGxlcihjYik7XHJcbiAgICB9XHJcbiAgICByZW1vdmVVc2VyU2Vzc2lvbkNoYW5nZWQoY2IpIHtcclxuICAgICAgICB0aGlzLl91c2VyU2Vzc2lvbkNoYW5nZWQucmVtb3ZlSGFuZGxlcihjYik7XHJcbiAgICB9XHJcbiAgICBfcmFpc2VVc2VyU2Vzc2lvbkNoYW5nZWQoKSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiVXNlck1hbmFnZXJFdmVudHMuX3JhaXNlVXNlclNlc3Npb25DaGFuZ2VkXCIpO1xyXG4gICAgICAgIHRoaXMuX3VzZXJTZXNzaW9uQ2hhbmdlZC5yYWlzZSgpO1xyXG4gICAgfVxyXG59XHJcbiIsIi8vIENvcHlyaWdodCAoYykgQnJvY2sgQWxsZW4gJiBEb21pbmljayBCYWllci4gQWxsIHJpZ2h0cyByZXNlcnZlZC5cclxuLy8gTGljZW5zZWQgdW5kZXIgdGhlIEFwYWNoZSBMaWNlbnNlLCBWZXJzaW9uIDIuMC4gU2VlIExJQ0VOU0UgaW4gdGhlIHByb2plY3Qgcm9vdCBmb3IgbGljZW5zZSBpbmZvcm1hdGlvbi5cclxuXHJcbmltcG9ydCB7IExvZyB9IGZyb20gJy4vTG9nLmpzJztcclxuaW1wb3J0IHsgT2lkY0NsaWVudFNldHRpbmdzIH0gZnJvbSAnLi9PaWRjQ2xpZW50U2V0dGluZ3MuanMnO1xyXG5pbXBvcnQgeyBSZWRpcmVjdE5hdmlnYXRvciB9IGZyb20gJy4vUmVkaXJlY3ROYXZpZ2F0b3IuanMnO1xyXG5pbXBvcnQgeyBQb3B1cE5hdmlnYXRvciB9IGZyb20gJy4vUG9wdXBOYXZpZ2F0b3IuanMnO1xyXG5pbXBvcnQgeyBJRnJhbWVOYXZpZ2F0b3IgfSBmcm9tICcuL0lGcmFtZU5hdmlnYXRvci5qcyc7XHJcbmltcG9ydCB7IFdlYlN0b3JhZ2VTdGF0ZVN0b3JlIH0gZnJvbSAnLi9XZWJTdG9yYWdlU3RhdGVTdG9yZS5qcyc7XHJcbmltcG9ydCB7IEdsb2JhbCB9IGZyb20gJy4vR2xvYmFsLmpzJztcclxuaW1wb3J0IHsgU2lnbmluUmVxdWVzdCB9IGZyb20gJy4vU2lnbmluUmVxdWVzdC5qcyc7XHJcblxyXG5jb25zdCBEZWZhdWx0QWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWUgPSA2MDtcclxuY29uc3QgRGVmYXVsdENoZWNrU2Vzc2lvbkludGVydmFsID0gMjAwMDtcclxuXHJcbmV4cG9ydCBjbGFzcyBVc2VyTWFuYWdlclNldHRpbmdzIGV4dGVuZHMgT2lkY0NsaWVudFNldHRpbmdzIHtcclxuICAgIGNvbnN0cnVjdG9yKHtcclxuICAgICAgICBwb3B1cF9yZWRpcmVjdF91cmksXHJcbiAgICAgICAgcG9wdXBfcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpLFxyXG4gICAgICAgIHBvcHVwV2luZG93RmVhdHVyZXMsXHJcbiAgICAgICAgcG9wdXBXaW5kb3dUYXJnZXQsXHJcbiAgICAgICAgc2lsZW50X3JlZGlyZWN0X3VyaSxcclxuICAgICAgICBzaWxlbnRSZXF1ZXN0VGltZW91dCxcclxuICAgICAgICBhdXRvbWF0aWNTaWxlbnRSZW5ldyA9IGZhbHNlLFxyXG4gICAgICAgIHZhbGlkYXRlU3ViT25TaWxlbnRSZW5ldyA9IGZhbHNlLFxyXG4gICAgICAgIGluY2x1ZGVJZFRva2VuSW5TaWxlbnRSZW5ldyA9IHRydWUsXHJcbiAgICAgICAgbW9uaXRvclNlc3Npb24gPSB0cnVlLFxyXG4gICAgICAgIG1vbml0b3JBbm9ueW1vdXNTZXNzaW9uID0gZmFsc2UsXHJcbiAgICAgICAgY2hlY2tTZXNzaW9uSW50ZXJ2YWwgPSBEZWZhdWx0Q2hlY2tTZXNzaW9uSW50ZXJ2YWwsXHJcbiAgICAgICAgc3RvcENoZWNrU2Vzc2lvbk9uRXJyb3IgPSB0cnVlLFxyXG4gICAgICAgIHF1ZXJ5X3N0YXR1c19yZXNwb25zZV90eXBlLFxyXG4gICAgICAgIHJldm9rZUFjY2Vzc1Rva2VuT25TaWdub3V0ID0gZmFsc2UsXHJcbiAgICAgICAgYWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWUgPSBEZWZhdWx0QWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWUsXHJcbiAgICAgICAgcmVkaXJlY3ROYXZpZ2F0b3IgPSBuZXcgUmVkaXJlY3ROYXZpZ2F0b3IoKSxcclxuICAgICAgICBwb3B1cE5hdmlnYXRvciA9IG5ldyBQb3B1cE5hdmlnYXRvcigpLFxyXG4gICAgICAgIGlmcmFtZU5hdmlnYXRvciA9IG5ldyBJRnJhbWVOYXZpZ2F0b3IoKSxcclxuICAgICAgICB1c2VyU3RvcmUgPSBuZXcgV2ViU3RvcmFnZVN0YXRlU3RvcmUoeyBzdG9yZTogR2xvYmFsLnNlc3Npb25TdG9yYWdlIH0pXHJcbiAgICB9ID0ge30pIHtcclxuICAgICAgICBzdXBlcihhcmd1bWVudHNbMF0pO1xyXG5cclxuICAgICAgICB0aGlzLl9wb3B1cF9yZWRpcmVjdF91cmkgPSBwb3B1cF9yZWRpcmVjdF91cmk7XHJcbiAgICAgICAgdGhpcy5fcG9wdXBfcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpID0gcG9wdXBfcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpO1xyXG4gICAgICAgIHRoaXMuX3BvcHVwV2luZG93RmVhdHVyZXMgPSBwb3B1cFdpbmRvd0ZlYXR1cmVzO1xyXG4gICAgICAgIHRoaXMuX3BvcHVwV2luZG93VGFyZ2V0ID0gcG9wdXBXaW5kb3dUYXJnZXQ7XHJcblxyXG4gICAgICAgIHRoaXMuX3NpbGVudF9yZWRpcmVjdF91cmkgPSBzaWxlbnRfcmVkaXJlY3RfdXJpO1xyXG4gICAgICAgIHRoaXMuX3NpbGVudFJlcXVlc3RUaW1lb3V0ID0gc2lsZW50UmVxdWVzdFRpbWVvdXQ7XHJcbiAgICAgICAgdGhpcy5fYXV0b21hdGljU2lsZW50UmVuZXcgPSBhdXRvbWF0aWNTaWxlbnRSZW5ldztcclxuICAgICAgICB0aGlzLl92YWxpZGF0ZVN1Yk9uU2lsZW50UmVuZXcgPSB2YWxpZGF0ZVN1Yk9uU2lsZW50UmVuZXc7XHJcbiAgICAgICAgdGhpcy5faW5jbHVkZUlkVG9rZW5JblNpbGVudFJlbmV3ID0gaW5jbHVkZUlkVG9rZW5JblNpbGVudFJlbmV3O1xyXG4gICAgICAgIHRoaXMuX2FjY2Vzc1Rva2VuRXhwaXJpbmdOb3RpZmljYXRpb25UaW1lID0gYWNjZXNzVG9rZW5FeHBpcmluZ05vdGlmaWNhdGlvblRpbWU7XHJcblxyXG4gICAgICAgIHRoaXMuX21vbml0b3JTZXNzaW9uID0gbW9uaXRvclNlc3Npb247XHJcbiAgICAgICAgdGhpcy5fbW9uaXRvckFub255bW91c1Nlc3Npb24gPSBtb25pdG9yQW5vbnltb3VzU2Vzc2lvbjtcclxuICAgICAgICB0aGlzLl9jaGVja1Nlc3Npb25JbnRlcnZhbCA9IGNoZWNrU2Vzc2lvbkludGVydmFsO1xyXG4gICAgICAgIHRoaXMuX3N0b3BDaGVja1Nlc3Npb25PbkVycm9yID0gc3RvcENoZWNrU2Vzc2lvbk9uRXJyb3I7XHJcbiAgICAgICAgaWYgKHF1ZXJ5X3N0YXR1c19yZXNwb25zZV90eXBlKSB7XHJcbiAgICAgICAgICAgIHRoaXMuX3F1ZXJ5X3N0YXR1c19yZXNwb25zZV90eXBlID0gcXVlcnlfc3RhdHVzX3Jlc3BvbnNlX3R5cGU7XHJcbiAgICAgICAgfSBcclxuICAgICAgICBlbHNlIGlmIChhcmd1bWVudHNbMF0gJiYgYXJndW1lbnRzWzBdLnJlc3BvbnNlX3R5cGUpIHtcclxuICAgICAgICAgICAgdGhpcy5fcXVlcnlfc3RhdHVzX3Jlc3BvbnNlX3R5cGUgPSBTaWduaW5SZXF1ZXN0LmlzT2lkYyhhcmd1bWVudHNbMF0ucmVzcG9uc2VfdHlwZSkgPyBcImlkX3Rva2VuXCIgOiBcImNvZGVcIjtcclxuICAgICAgICB9XHJcbiAgICAgICAgZWxzZSB7XHJcbiAgICAgICAgICAgIHRoaXMuX3F1ZXJ5X3N0YXR1c19yZXNwb25zZV90eXBlID0gXCJpZF90b2tlblwiO1xyXG4gICAgICAgIH1cclxuICAgICAgICB0aGlzLl9yZXZva2VBY2Nlc3NUb2tlbk9uU2lnbm91dCA9IHJldm9rZUFjY2Vzc1Rva2VuT25TaWdub3V0O1xyXG5cclxuICAgICAgICB0aGlzLl9yZWRpcmVjdE5hdmlnYXRvciA9IHJlZGlyZWN0TmF2aWdhdG9yO1xyXG4gICAgICAgIHRoaXMuX3BvcHVwTmF2aWdhdG9yID0gcG9wdXBOYXZpZ2F0b3I7XHJcbiAgICAgICAgdGhpcy5faWZyYW1lTmF2aWdhdG9yID0gaWZyYW1lTmF2aWdhdG9yO1xyXG5cclxuICAgICAgICB0aGlzLl91c2VyU3RvcmUgPSB1c2VyU3RvcmU7XHJcbiAgICB9XHJcblxyXG4gICAgZ2V0IHBvcHVwX3JlZGlyZWN0X3VyaSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fcG9wdXBfcmVkaXJlY3RfdXJpO1xyXG4gICAgfVxyXG4gICAgZ2V0IHBvcHVwX3Bvc3RfbG9nb3V0X3JlZGlyZWN0X3VyaSgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fcG9wdXBfcG9zdF9sb2dvdXRfcmVkaXJlY3RfdXJpO1xyXG4gICAgfVxyXG4gICAgZ2V0IHBvcHVwV2luZG93RmVhdHVyZXMoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3BvcHVwV2luZG93RmVhdHVyZXM7XHJcbiAgICB9XHJcbiAgICBnZXQgcG9wdXBXaW5kb3dUYXJnZXQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3BvcHVwV2luZG93VGFyZ2V0O1xyXG4gICAgfVxyXG5cclxuICAgIGdldCBzaWxlbnRfcmVkaXJlY3RfdXJpKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9zaWxlbnRfcmVkaXJlY3RfdXJpO1xyXG4gICAgfVxyXG4gICAgIGdldCBzaWxlbnRSZXF1ZXN0VGltZW91dCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc2lsZW50UmVxdWVzdFRpbWVvdXQ7XHJcbiAgICB9XHJcbiAgICBnZXQgYXV0b21hdGljU2lsZW50UmVuZXcoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX2F1dG9tYXRpY1NpbGVudFJlbmV3O1xyXG4gICAgfVxyXG4gICAgZ2V0IHZhbGlkYXRlU3ViT25TaWxlbnRSZW5ldygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fdmFsaWRhdGVTdWJPblNpbGVudFJlbmV3O1xyXG4gICAgfVxyXG4gICAgZ2V0IGluY2x1ZGVJZFRva2VuSW5TaWxlbnRSZW5ldygpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5faW5jbHVkZUlkVG9rZW5JblNpbGVudFJlbmV3O1xyXG4gICAgfVxyXG4gICAgZ2V0IGFjY2Vzc1Rva2VuRXhwaXJpbmdOb3RpZmljYXRpb25UaW1lKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9hY2Nlc3NUb2tlbkV4cGlyaW5nTm90aWZpY2F0aW9uVGltZTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQgbW9uaXRvclNlc3Npb24oKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX21vbml0b3JTZXNzaW9uO1xyXG4gICAgfVxyXG4gICAgZ2V0IG1vbml0b3JBbm9ueW1vdXNTZXNzaW9uKCkge1xyXG4gICAgICAgIHJldHVybiB0aGlzLl9tb25pdG9yQW5vbnltb3VzU2Vzc2lvbjtcclxuICAgIH1cclxuICAgIGdldCBjaGVja1Nlc3Npb25JbnRlcnZhbCgpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fY2hlY2tTZXNzaW9uSW50ZXJ2YWw7XHJcbiAgICB9XHJcbiAgICBnZXQgc3RvcENoZWNrU2Vzc2lvbk9uRXJyb3IoKXtcclxuICAgICAgICByZXR1cm4gdGhpcy5fc3RvcENoZWNrU2Vzc2lvbk9uRXJyb3I7XHJcbiAgICB9XHJcbiAgICBnZXQgcXVlcnlfc3RhdHVzX3Jlc3BvbnNlX3R5cGUoKXtcclxuICAgICAgICByZXR1cm4gdGhpcy5fcXVlcnlfc3RhdHVzX3Jlc3BvbnNlX3R5cGU7XHJcbiAgICB9XHJcbiAgICBnZXQgcmV2b2tlQWNjZXNzVG9rZW5PblNpZ25vdXQoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3Jldm9rZUFjY2Vzc1Rva2VuT25TaWdub3V0O1xyXG4gICAgfVxyXG5cclxuICAgIGdldCByZWRpcmVjdE5hdmlnYXRvcigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5fcmVkaXJlY3ROYXZpZ2F0b3I7XHJcbiAgICB9XHJcbiAgICBnZXQgcG9wdXBOYXZpZ2F0b3IoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3BvcHVwTmF2aWdhdG9yO1xyXG4gICAgfVxyXG4gICAgZ2V0IGlmcmFtZU5hdmlnYXRvcigpIHtcclxuICAgICAgICByZXR1cm4gdGhpcy5faWZyYW1lTmF2aWdhdG9yO1xyXG4gICAgfVxyXG5cclxuICAgIGdldCB1c2VyU3RvcmUoKSB7XHJcbiAgICAgICAgcmV0dXJuIHRoaXMuX3VzZXJTdG9yZTtcclxuICAgIH1cclxufVxyXG4iLCIvLyBDb3B5cmlnaHQgKGMpIEJyb2NrIEFsbGVuICYgRG9taW5pY2sgQmFpZXIuIEFsbCByaWdodHMgcmVzZXJ2ZWQuXHJcbi8vIExpY2Vuc2VkIHVuZGVyIHRoZSBBcGFjaGUgTGljZW5zZSwgVmVyc2lvbiAyLjAuIFNlZSBMSUNFTlNFIGluIHRoZSBwcm9qZWN0IHJvb3QgZm9yIGxpY2Vuc2UgaW5mb3JtYXRpb24uXHJcblxyXG5pbXBvcnQgeyBMb2cgfSBmcm9tICcuL0xvZy5qcyc7XHJcbmltcG9ydCB7IEdsb2JhbCB9IGZyb20gJy4vR2xvYmFsLmpzJztcclxuXHJcbmV4cG9ydCBjbGFzcyBXZWJTdG9yYWdlU3RhdGVTdG9yZSB7XHJcbiAgICBjb25zdHJ1Y3Rvcih7cHJlZml4ID0gXCJvaWRjLlwiLCBzdG9yZSA9IEdsb2JhbC5sb2NhbFN0b3JhZ2V9ID0ge30pIHtcclxuICAgICAgICB0aGlzLl9zdG9yZSA9IHN0b3JlO1xyXG4gICAgICAgIHRoaXMuX3ByZWZpeCA9IHByZWZpeDtcclxuICAgIH1cclxuXHJcbiAgICBzZXQoa2V5LCB2YWx1ZSkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIldlYlN0b3JhZ2VTdGF0ZVN0b3JlLnNldFwiLCBrZXkpO1xyXG5cclxuICAgICAgICBrZXkgPSB0aGlzLl9wcmVmaXggKyBrZXk7XHJcblxyXG4gICAgICAgIHRoaXMuX3N0b3JlLnNldEl0ZW0oa2V5LCB2YWx1ZSk7XHJcblxyXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoKTtcclxuICAgIH1cclxuXHJcbiAgICBnZXQoa2V5KSB7XHJcbiAgICAgICAgTG9nLmRlYnVnKFwiV2ViU3RvcmFnZVN0YXRlU3RvcmUuZ2V0XCIsIGtleSk7XHJcblxyXG4gICAgICAgIGtleSA9IHRoaXMuX3ByZWZpeCArIGtleTtcclxuXHJcbiAgICAgICAgbGV0IGl0ZW0gPSB0aGlzLl9zdG9yZS5nZXRJdGVtKGtleSk7XHJcblxyXG4gICAgICAgIHJldHVybiBQcm9taXNlLnJlc29sdmUoaXRlbSk7XHJcbiAgICB9XHJcblxyXG4gICAgcmVtb3ZlKGtleSkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIldlYlN0b3JhZ2VTdGF0ZVN0b3JlLnJlbW92ZVwiLCBrZXkpO1xyXG5cclxuICAgICAgICBrZXkgPSB0aGlzLl9wcmVmaXggKyBrZXk7XHJcblxyXG4gICAgICAgIGxldCBpdGVtID0gdGhpcy5fc3RvcmUuZ2V0SXRlbShrZXkpO1xyXG4gICAgICAgIHRoaXMuX3N0b3JlLnJlbW92ZUl0ZW0oa2V5KTtcclxuXHJcbiAgICAgICAgcmV0dXJuIFByb21pc2UucmVzb2x2ZShpdGVtKTtcclxuICAgIH1cclxuXHJcbiAgICBnZXRBbGxLZXlzKCkge1xyXG4gICAgICAgIExvZy5kZWJ1ZyhcIldlYlN0b3JhZ2VTdGF0ZVN0b3JlLmdldEFsbEtleXNcIik7XHJcblxyXG4gICAgICAgIHZhciBrZXlzID0gW107XHJcblxyXG4gICAgICAgIGZvciAobGV0IGluZGV4ID0gMDsgaW5kZXggPCB0aGlzLl9zdG9yZS5sZW5ndGg7IGluZGV4KyspIHtcclxuICAgICAgICAgICAgbGV0IGtleSA9IHRoaXMuX3N0b3JlLmtleShpbmRleCk7XHJcblxyXG4gICAgICAgICAgICBpZiAoa2V5LmluZGV4T2YodGhpcy5fcHJlZml4KSA9PT0gMCkge1xyXG4gICAgICAgICAgICAgICAga2V5cy5wdXNoKGtleS5zdWJzdHIodGhpcy5fcHJlZml4Lmxlbmd0aCkpO1xyXG4gICAgICAgICAgICB9XHJcbiAgICAgICAgfVxyXG5cclxuICAgICAgICByZXR1cm4gUHJvbWlzZS5yZXNvbHZlKGtleXMpO1xyXG4gICAgfVxyXG59XHJcbiIsImltcG9ydCB7IGp3cywgS0VZVVRJTCBhcyBLZXlVdGlsLCBYNTA5LCBjcnlwdG8sIGhleHRvYjY0dSwgYjY0dG9oZXggfSBmcm9tICcuLi8uLi9qc3JzYXNpZ24vZGlzdC9qc3JzYXNpZ24uanMnO1xyXG5cclxuY29uc3QgQWxsb3dlZFNpZ25pbmdBbGdzID0gWydSUzI1NicsICdSUzM4NCcsICdSUzUxMicsICdQUzI1NicsICdQUzM4NCcsICdQUzUxMicsICdFUzI1NicsICdFUzM4NCcsICdFUzUxMiddO1xyXG5cclxuZXhwb3J0IHtcclxuICAgIGp3cyxcclxuICAgIEtleVV0aWwsXHJcbiAgICBYNTA5LFxyXG4gICAgY3J5cHRvLFxyXG4gICAgaGV4dG9iNjR1LFxyXG4gICAgYjY0dG9oZXgsXHJcbiAgICBBbGxvd2VkU2lnbmluZ0FsZ3NcclxufTtcclxuIiwiaW1wb3J0IHV1aWQ0IGZyb20gJ3V1aWQvdjQnO1xyXG5cclxuLyoqXHJcbiAqIEdlbmVyYXRlcyBSRkM0MTIyIHZlcnNpb24gNCBndWlkICgpXHJcbiAqL1xyXG5cclxuZXhwb3J0IGRlZmF1bHQgZnVuY3Rpb24gcmFuZG9tKCkge1xyXG4gIHJldHVybiB1dWlkNCgpLnJlcGxhY2UoLy0vZywgJycpO1xyXG59XHJcbiIsImNvbnN0IFZlcnNpb24gPSBcIjEuMTAuMVwiOyBleHBvcnQge1ZlcnNpb259OyJdLCJzb3VyY2VSb290IjoiIn0=
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Controllers/HomeController.cs
    ================================================
    /*
     Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ 
    
     Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
    
     Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 
     Source code and license this software can be found 
    
     The above copyright notice and this permission notice shall be included in all
     copies or substantial portions of the Software.
    */
    
    public class HomeController : Controller
    {
        private readonly ILogger _logger;
    
        public HomeController(ILogger logger)
        {
            _logger = logger;
        }
    
        public IActionResult Index()
        {
            return View();
        }
    
        public async Task CallApi()
        {
            var accessToken = await HttpContext.GetTokenAsync("access_token");
    
            var client = new HttpClient();
            client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken);
            var content = await client.GetStringAsync("https://localhost:6001/identity");
    
            var obj = JsonSerializer.Deserialize(content);
            ViewBag.Json = obj.ToString();
            return View("json");
        }
    
        public IActionResult Logout()
        {
            return SignOut("Cookies", "oidc");
        }
    
        [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)]
        public IActionResult Error()
        {
            return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier });
        }
    }
    
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/GlobalUsings.cs
    ================================================
    /*
     Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ 
    
     Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
    
     Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 
     Source code and license this software can be found 
    
     The above copyright notice and this permission notice shall be included in all
     copies or substantial portions of the Software.
    */
    
    global using Microsoft.AspNetCore.Authentication;
    global using Microsoft.AspNetCore.Mvc;
    global using System.Diagnostics;
    global using System.IdentityModel.Tokens.Jwt;
    global using System.Net.Http.Headers;
    global using System.Text.Json;
    
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Models/ErrorViewModel.cs
    ================================================
    /*
     Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ 
    
     Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
    
     Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 
     Source code and license this software can be found 
    
     The above copyright notice and this permission notice shall be included in all
     copies or substantial portions of the Software.
    */
    
    public class ErrorViewModel
    {
        public string RequestId { get; set; }
    
        public bool ShowRequestId => !string.IsNullOrEmpty(RequestId);
    }
    
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/MvcClient.csproj
    ================================================
    
    
      
        
        
      
    
      
        
          true
          PreserveNewest
        
      
    
    
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Program.cs
    ================================================
    /*
     Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ 
    
     Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
    
     Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 
     Source code and license this software can be found 
    
     The above copyright notice and this permission notice shall be included in all
     copies or substantial portions of the Software.
    */
    
    JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
    
    
    var builder = WebApplication.CreateBuilder(args);
    
    builder.Services.AddControllersWithViews();
    builder.Services
        .AddAuthentication(options =>
        {
            options.DefaultScheme = "Cookies";
            options.DefaultChallengeScheme = "oidc";
        })
        .AddCookie("Cookies")
        .AddOpenIdConnect("oidc", options =>
        {
            options.Authority = "https://localhost:5001";
    
            options.ClientId = "mvc";
            options.ClientSecret = "secret";
            options.ResponseType = "code";
    
            options.Scope.Add("api1");
    
            options.SaveTokens = true;
        });
    
    
    using (var app = builder.Build())
    {
        if (app.Environment.IsDevelopment())
            app.UseDeveloperExceptionPage();
        else
            app.UseExceptionHandler("/Home/Error");
    
        app.UseStaticFiles();
        app.UseRouting();
        app.UseAuthentication();
        app.UseAuthorization();
        app.MapDefaultControllerRoute().RequireAuthorization();
    
        await app.RunAsync();
    }
    
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Properties/launchSettings.json
    ================================================
    {
      "profiles": {
        "MvcClient": {
          "commandName": "Project",
          "launchBrowser": true,
          "environmentVariables": {
            "ASPNETCORE_ENVIRONMENT": "Development"
          },
          "applicationUrl": "https://localhost:5002"
        }
      }
    }
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Startup.cs
    ================================================
    /*
     Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ 
    
     Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved.
    
     Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. 
     Source code and license this software can be found 
    
     The above copyright notice and this permission notice shall be included in all
     copies or substantial portions of the Software.
    */
    
    using Microsoft.AspNetCore.Builder;
    using Microsoft.AspNetCore.Hosting;
    using Microsoft.Extensions.DependencyInjection;
    using Microsoft.Extensions.Hosting;
    using System.IdentityModel.Tokens.Jwt;
    
    namespace MvcClient
    {
        public class Startup
        {
            public void ConfigureServices(IServiceCollection services)
            {
                services.AddControllersWithViews();
    
                JwtSecurityTokenHandler.DefaultMapInboundClaims = false;
    
                services.AddAuthentication(options =>
                {
                    options.DefaultScheme = "Cookies";
                    options.DefaultChallengeScheme = "oidc";
                })
                .AddCookie("Cookies")
                .AddOpenIdConnect("oidc", options =>
                {
                    options.Authority = "https://localhost:5001";
    
                    options.ClientId = "mvc";
                    options.ClientSecret = "secret";
                    options.ResponseType = "code";
                    
                    options.Scope.Add("api1");
    
                    options.SaveTokens = true;
                });
            }
    
            public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
            {
                if (env.IsDevelopment())
                {
                    app.UseDeveloperExceptionPage();
                }
                else
                {
                    app.UseExceptionHandler("/Home/Error");
                }
    
                app.UseStaticFiles();
    
                app.UseRouting();
                app.UseAuthentication();
                app.UseAuthorization();
    
                app.UseEndpoints(endpoints =>
                {
                    endpoints.MapDefaultControllerRoute()
                        .RequireAuthorization();
                });
            }
        }
    }
    
    
    ================================================
    FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Views/Home/Index.cshtml
    ================================================
    @using Microsoft.AspNetCore.Authentication
    
    

    Claims

    @foreach (var claim in User.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items) {
    @prop.Key
    @prop.Value
    }
    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Views/Home/Privacy.cshtml ================================================ @{ ViewData["Title"] = "Privacy Policy"; }

    @ViewData["Title"]

    Use this page to detail your site's privacy policy.

    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ ViewData["Title"] = "Error"; }

    Error.

    An error occurred while processing your request.

    @if (Model.ShowRequestId) {

    Request ID: @Model.RequestId

    }

    Development Mode

    Swapping to Development environment will display more detailed information about the error that occurred.

    The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app.

    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Views/Shared/_Layout.cshtml ================================================ @ViewData["Title"] - MvcClient
    @RenderBody()
    @RenderSection("Scripts", required: false) ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Views/Shared/_ValidationScriptsPartial.cshtml ================================================  ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Views/Shared/json.cshtml ================================================
    @ViewBag.Json
    ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Views/_ViewImports.cshtml ================================================ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/appsettings.json ================================================ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/wwwroot/css/site.css ================================================ /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification for details on configuring this project to bundle and minify static web assets. */ a.navbar-brand { white-space: normal; text-align: center; word-break: break-all; } /* Provide sufficient contrast against white background */ a { color: #0366d6; } .btn-primary { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } /* Sticky footer styles -------------------------------------------------- */ html { font-size: 14px; } @media (min-width: 768px) { html { font-size: 16px; } } .border-top { border-top: 1px solid #e5e5e5; } .border-bottom { border-bottom: 1px solid #e5e5e5; } .box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); } button.accept-policy { font-size: 1rem; line-height: inherit; } /* Sticky footer styles -------------------------------------------------- */ html { position: relative; min-height: 100%; } body { /* Margin bottom by footer height */ margin-bottom: 60px; } .footer { position: absolute; bottom: 0; width: 100%; white-space: nowrap; line-height: 60px; /* Vertically center the text there */ } ================================================ FILE: samples/Quickstarts/4_JavaScriptClient/src/MvcClient/wwwroot/js/site.js ================================================ // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification // for details on configuring this project to bundle and minify static web assets. // Write your JavaScript code. ================================================ FILE: samples/Quickstarts/5_EntityFramework/Quickstart.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34322.80 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BD8BA25C-A91D-419D-99B6-B575ADD6D061}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer", "src\IdentityServer\IdentityServer.csproj", "{79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvcClient", "src\MvcClient\MvcClient.csproj", "{BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api", "..\Shared\src\Api\Api.csproj", "{F555BE49-9B75-4379-96A5-A0490B18EDDB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "..\Shared\src\Client\Client.csproj", "{EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|Any CPU.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x64.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x64.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x86.ActiveCfg = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Debug|x86.Build.0 = Debug|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|Any CPU.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|Any CPU.Build.0 = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x64.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x64.Build.0 = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x86.ActiveCfg = Release|Any CPU {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E}.Release|x86.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|Any CPU.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x64.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x64.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x86.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x86.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|Any CPU.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x64.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x64.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x86.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x86.Build.0 = Release|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Debug|Any CPU.Build.0 = Debug|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Debug|x64.ActiveCfg = Debug|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Debug|x64.Build.0 = Debug|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Debug|x86.ActiveCfg = Debug|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Debug|x86.Build.0 = Debug|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Release|Any CPU.ActiveCfg = Release|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Release|Any CPU.Build.0 = Release|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Release|x64.ActiveCfg = Release|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Release|x64.Build.0 = Release|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Release|x86.ActiveCfg = Release|Any CPU {F555BE49-9B75-4379-96A5-A0490B18EDDB}.Release|x86.Build.0 = Release|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Debug|Any CPU.Build.0 = Debug|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Debug|x64.ActiveCfg = Debug|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Debug|x64.Build.0 = Debug|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Debug|x86.ActiveCfg = Debug|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Debug|x86.Build.0 = Debug|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Release|Any CPU.ActiveCfg = Release|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Release|Any CPU.Build.0 = Release|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Release|x64.ActiveCfg = Release|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Release|x64.Build.0 = Release|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Release|x86.ActiveCfg = Release|Any CPU {EFC08DC4-FF0B-4B06-A784-4F85C4CCB41C}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {79D1F107-F3AF-4A93-BF5D-49EF6A46E50E} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {84D5AF06-1243-46F1-9D58-70ED572832C3} EndGlobalSection EndGlobal ================================================ FILE: samples/Quickstarts/5_EntityFramework/Quickstart.sln.licenseheader ================================================ extensions: designer.cs generated.cs extensions: .cs /* Copyright (c) 2024 HigginsSoft Written by Alexander Higgins https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code for this software can be found at https://github.com/alexhiggins732/IdentityServer8 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Config.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Secret = IdentityServer8.Models.Secret; public static class Config { public static IEnumerable IdentityResources => new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable ApiScopes => new List { new ApiScope("api1", "My API") }; public static IEnumerable Clients => new List { // machine to machine client new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, // scopes that client has access to AllowedScopes = { "api1" } }, // interactive ASP.NET Core MVC client new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // where to redirect to after login RedirectUris = { "https://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } } }; } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Data/Migrations/IdentityServer/ConfigurationDb/20200625203625_InitialIdentityServerConfigurationDbMigration.Designer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; namespace IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb { [DbContext(typeof(ConfigurationDbContext))] [Migration("20200625203625_InitialIdentityServerConfigurationDbMigration")] partial class InitialIdentityServerConfigurationDbMigration { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AllowedAccessTokenSigningAlgorithms") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Enabled") .HasColumnType("bit"); b.Property("LastAccessed") .HasColumnType("datetime2"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("NonEditable") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("ApiResources"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Scope") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(4000)") .HasMaxLength(4000); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceSecrets"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Emphasize") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Required") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("ApiScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ScopeId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ScopeId"); b.ToTable("ApiScopeClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("ScopeId") .HasColumnType("int"); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ScopeId"); b.ToTable("ApiScopeProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AbsoluteRefreshTokenLifetime") .HasColumnType("int"); b.Property("AccessTokenLifetime") .HasColumnType("int"); b.Property("AccessTokenType") .HasColumnType("int"); b.Property("AllowAccessTokensViaBrowser") .HasColumnType("bit"); b.Property("AllowOfflineAccess") .HasColumnType("bit"); b.Property("AllowPlainTextPkce") .HasColumnType("bit"); b.Property("AllowRememberConsent") .HasColumnType("bit"); b.Property("AllowedIdentityTokenSigningAlgorithms") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("AlwaysIncludeUserClaimsInIdToken") .HasColumnType("bit"); b.Property("AlwaysSendClientClaims") .HasColumnType("bit"); b.Property("AuthorizationCodeLifetime") .HasColumnType("int"); b.Property("BackChannelLogoutSessionRequired") .HasColumnType("bit"); b.Property("BackChannelLogoutUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("ClientClaimsPrefix") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("ConsentLifetime") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DeviceCodeLifetime") .HasColumnType("int"); b.Property("EnableLocalLogin") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("FrontChannelLogoutSessionRequired") .HasColumnType("bit"); b.Property("FrontChannelLogoutUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("IdentityTokenLifetime") .HasColumnType("int"); b.Property("IncludeJwtId") .HasColumnType("bit"); b.Property("LastAccessed") .HasColumnType("datetime2"); b.Property("LogoUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("NonEditable") .HasColumnType("bit"); b.Property("PairWiseSubjectSalt") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ProtocolType") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("RefreshTokenExpiration") .HasColumnType("int"); b.Property("RefreshTokenUsage") .HasColumnType("int"); b.Property("RequireClientSecret") .HasColumnType("bit"); b.Property("RequireConsent") .HasColumnType("bit"); b.Property("RequirePkce") .HasColumnType("bit"); b.Property("RequireRequestObject") .HasColumnType("bit"); b.Property("SlidingRefreshTokenLifetime") .HasColumnType("int"); b.Property("UpdateAccessTokenClaimsOnRefresh") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.Property("UserCodeType") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("UserSsoLifetime") .HasColumnType("int"); b.HasKey("Id"); b.HasIndex("ClientId") .IsUnique(); b.ToTable("Clients"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Origin") .IsRequired() .HasColumnType("nvarchar(150)") .HasMaxLength(150); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientCorsOrigins"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("GrantType") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientGrantTypes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Provider") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientIdPRestrictions"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("PostLogoutRedirectUri") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientPostLogoutRedirectUris"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("RedirectUri") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientRedirectUris"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Scope") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(4000)") .HasMaxLength(4000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientSecrets"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Emphasize") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("NonEditable") .HasColumnType("bit"); b.Property("Required") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("IdentityResources"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("IdentityResourceId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("IdentityResourceId"); b.ToTable("IdentityResourceClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("IdentityResourceId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("IdentityResourceId"); b.ToTable("IdentityResourceProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("UserClaims") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Properties") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Scopes") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Secrets") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") .WithMany("UserClaims") .HasForeignKey("ScopeId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") .WithMany("Properties") .HasForeignKey("ScopeId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("Claims") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedCorsOrigins") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedGrantTypes") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("IdentityProviderRestrictions") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("PostLogoutRedirectUris") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("Properties") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("RedirectUris") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedScopes") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("ClientSecrets") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") .WithMany("UserClaims") .HasForeignKey("IdentityResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") .WithMany("Properties") .HasForeignKey("IdentityResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); #pragma warning restore 612, 618 } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Data/Migrations/IdentityServer/ConfigurationDb/20200625203625_InitialIdentityServerConfigurationDbMigration.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore.Migrations; namespace IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb { public partial class InitialIdentityServerConfigurationDbMigration : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "ApiResources", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Enabled = table.Column(nullable: false), Name = table.Column(maxLength: 200, nullable: false), DisplayName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), AllowedAccessTokenSigningAlgorithms = table.Column(maxLength: 100, nullable: true), ShowInDiscoveryDocument = table.Column(nullable: false), Created = table.Column(nullable: false), Updated = table.Column(nullable: true), LastAccessed = table.Column(nullable: true), NonEditable = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResources", x => x.Id); }); migrationBuilder.CreateTable( name: "ApiScopes", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Enabled = table.Column(nullable: false), Name = table.Column(maxLength: 200, nullable: false), DisplayName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), Required = table.Column(nullable: false), Emphasize = table.Column(nullable: false), ShowInDiscoveryDocument = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiScopes", x => x.Id); }); migrationBuilder.CreateTable( name: "Clients", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Enabled = table.Column(nullable: false), ClientId = table.Column(maxLength: 200, nullable: false), ProtocolType = table.Column(maxLength: 200, nullable: false), RequireClientSecret = table.Column(nullable: false), ClientName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), ClientUri = table.Column(maxLength: 2000, nullable: true), LogoUri = table.Column(maxLength: 2000, nullable: true), RequireConsent = table.Column(nullable: false), AllowRememberConsent = table.Column(nullable: false), AlwaysIncludeUserClaimsInIdToken = table.Column(nullable: false), RequirePkce = table.Column(nullable: false), AllowPlainTextPkce = table.Column(nullable: false), RequireRequestObject = table.Column(nullable: false), AllowAccessTokensViaBrowser = table.Column(nullable: false), FrontChannelLogoutUri = table.Column(maxLength: 2000, nullable: true), FrontChannelLogoutSessionRequired = table.Column(nullable: false), BackChannelLogoutUri = table.Column(maxLength: 2000, nullable: true), BackChannelLogoutSessionRequired = table.Column(nullable: false), AllowOfflineAccess = table.Column(nullable: false), IdentityTokenLifetime = table.Column(nullable: false), AllowedIdentityTokenSigningAlgorithms = table.Column(maxLength: 100, nullable: true), AccessTokenLifetime = table.Column(nullable: false), AuthorizationCodeLifetime = table.Column(nullable: false), ConsentLifetime = table.Column(nullable: true), AbsoluteRefreshTokenLifetime = table.Column(nullable: false), SlidingRefreshTokenLifetime = table.Column(nullable: false), RefreshTokenUsage = table.Column(nullable: false), UpdateAccessTokenClaimsOnRefresh = table.Column(nullable: false), RefreshTokenExpiration = table.Column(nullable: false), AccessTokenType = table.Column(nullable: false), EnableLocalLogin = table.Column(nullable: false), IncludeJwtId = table.Column(nullable: false), AlwaysSendClientClaims = table.Column(nullable: false), ClientClaimsPrefix = table.Column(maxLength: 200, nullable: true), PairWiseSubjectSalt = table.Column(maxLength: 200, nullable: true), Created = table.Column(nullable: false), Updated = table.Column(nullable: true), LastAccessed = table.Column(nullable: true), UserSsoLifetime = table.Column(nullable: true), UserCodeType = table.Column(maxLength: 100, nullable: true), DeviceCodeLifetime = table.Column(nullable: false), NonEditable = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_Clients", x => x.Id); }); migrationBuilder.CreateTable( name: "IdentityResources", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Enabled = table.Column(nullable: false), Name = table.Column(maxLength: 200, nullable: false), DisplayName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), Required = table.Column(nullable: false), Emphasize = table.Column(nullable: false), ShowInDiscoveryDocument = table.Column(nullable: false), Created = table.Column(nullable: false), Updated = table.Column(nullable: true), NonEditable = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_IdentityResources", x => x.Id); }); migrationBuilder.CreateTable( name: "ApiResourceClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Type = table.Column(maxLength: 200, nullable: false), ApiResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResourceClaims", x => x.Id); table.ForeignKey( name: "FK_ApiResourceClaims_ApiResources_ApiResourceId", column: x => x.ApiResourceId, principalTable: "ApiResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiResourceProperties", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Key = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 2000, nullable: false), ApiResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResourceProperties", x => x.Id); table.ForeignKey( name: "FK_ApiResourceProperties_ApiResources_ApiResourceId", column: x => x.ApiResourceId, principalTable: "ApiResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiResourceScopes", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Scope = table.Column(maxLength: 200, nullable: false), ApiResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResourceScopes", x => x.Id); table.ForeignKey( name: "FK_ApiResourceScopes_ApiResources_ApiResourceId", column: x => x.ApiResourceId, principalTable: "ApiResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiResourceSecrets", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Description = table.Column(maxLength: 1000, nullable: true), Value = table.Column(maxLength: 4000, nullable: false), Expiration = table.Column(nullable: true), Type = table.Column(maxLength: 250, nullable: false), Created = table.Column(nullable: false), ApiResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResourceSecrets", x => x.Id); table.ForeignKey( name: "FK_ApiResourceSecrets_ApiResources_ApiResourceId", column: x => x.ApiResourceId, principalTable: "ApiResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiScopeClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Type = table.Column(maxLength: 200, nullable: false), ScopeId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiScopeClaims", x => x.Id); table.ForeignKey( name: "FK_ApiScopeClaims_ApiScopes_ScopeId", column: x => x.ScopeId, principalTable: "ApiScopes", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiScopeProperties", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Key = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 2000, nullable: false), ScopeId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiScopeProperties", x => x.Id); table.ForeignKey( name: "FK_ApiScopeProperties_ApiScopes_ScopeId", column: x => x.ScopeId, principalTable: "ApiScopes", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Type = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 250, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientClaims", x => x.Id); table.ForeignKey( name: "FK_ClientClaims_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientCorsOrigins", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Origin = table.Column(maxLength: 150, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientCorsOrigins", x => x.Id); table.ForeignKey( name: "FK_ClientCorsOrigins_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientGrantTypes", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), GrantType = table.Column(maxLength: 250, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientGrantTypes", x => x.Id); table.ForeignKey( name: "FK_ClientGrantTypes_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientIdPRestrictions", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Provider = table.Column(maxLength: 200, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientIdPRestrictions", x => x.Id); table.ForeignKey( name: "FK_ClientIdPRestrictions_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientPostLogoutRedirectUris", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), PostLogoutRedirectUri = table.Column(maxLength: 2000, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientPostLogoutRedirectUris", x => x.Id); table.ForeignKey( name: "FK_ClientPostLogoutRedirectUris_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientProperties", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Key = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 2000, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientProperties", x => x.Id); table.ForeignKey( name: "FK_ClientProperties_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientRedirectUris", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), RedirectUri = table.Column(maxLength: 2000, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientRedirectUris", x => x.Id); table.ForeignKey( name: "FK_ClientRedirectUris_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientScopes", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Scope = table.Column(maxLength: 200, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientScopes", x => x.Id); table.ForeignKey( name: "FK_ClientScopes_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientSecrets", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Description = table.Column(maxLength: 2000, nullable: true), Value = table.Column(maxLength: 4000, nullable: false), Expiration = table.Column(nullable: true), Type = table.Column(maxLength: 250, nullable: false), Created = table.Column(nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientSecrets", x => x.Id); table.ForeignKey( name: "FK_ClientSecrets_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "IdentityResourceClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Type = table.Column(maxLength: 200, nullable: false), IdentityResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_IdentityResourceClaims", x => x.Id); table.ForeignKey( name: "FK_IdentityResourceClaims_IdentityResources_IdentityResourceId", column: x => x.IdentityResourceId, principalTable: "IdentityResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "IdentityResourceProperties", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Key = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 2000, nullable: false), IdentityResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_IdentityResourceProperties", x => x.Id); table.ForeignKey( name: "FK_IdentityResourceProperties_IdentityResources_IdentityResourceId", column: x => x.IdentityResourceId, principalTable: "IdentityResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( name: "IX_ApiResourceClaims_ApiResourceId", table: "ApiResourceClaims", column: "ApiResourceId"); migrationBuilder.CreateIndex( name: "IX_ApiResourceProperties_ApiResourceId", table: "ApiResourceProperties", column: "ApiResourceId"); migrationBuilder.CreateIndex( name: "IX_ApiResources_Name", table: "ApiResources", column: "Name", unique: true); migrationBuilder.CreateIndex( name: "IX_ApiResourceScopes_ApiResourceId", table: "ApiResourceScopes", column: "ApiResourceId"); migrationBuilder.CreateIndex( name: "IX_ApiResourceSecrets_ApiResourceId", table: "ApiResourceSecrets", column: "ApiResourceId"); migrationBuilder.CreateIndex( name: "IX_ApiScopeClaims_ScopeId", table: "ApiScopeClaims", column: "ScopeId"); migrationBuilder.CreateIndex( name: "IX_ApiScopeProperties_ScopeId", table: "ApiScopeProperties", column: "ScopeId"); migrationBuilder.CreateIndex( name: "IX_ApiScopes_Name", table: "ApiScopes", column: "Name", unique: true); migrationBuilder.CreateIndex( name: "IX_ClientClaims_ClientId", table: "ClientClaims", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientCorsOrigins_ClientId", table: "ClientCorsOrigins", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientGrantTypes_ClientId", table: "ClientGrantTypes", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientIdPRestrictions_ClientId", table: "ClientIdPRestrictions", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientPostLogoutRedirectUris_ClientId", table: "ClientPostLogoutRedirectUris", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientProperties_ClientId", table: "ClientProperties", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientRedirectUris_ClientId", table: "ClientRedirectUris", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_Clients_ClientId", table: "Clients", column: "ClientId", unique: true); migrationBuilder.CreateIndex( name: "IX_ClientScopes_ClientId", table: "ClientScopes", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientSecrets_ClientId", table: "ClientSecrets", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_IdentityResourceClaims_IdentityResourceId", table: "IdentityResourceClaims", column: "IdentityResourceId"); migrationBuilder.CreateIndex( name: "IX_IdentityResourceProperties_IdentityResourceId", table: "IdentityResourceProperties", column: "IdentityResourceId"); migrationBuilder.CreateIndex( name: "IX_IdentityResources_Name", table: "IdentityResources", column: "Name", unique: true); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "ApiResourceClaims"); migrationBuilder.DropTable( name: "ApiResourceProperties"); migrationBuilder.DropTable( name: "ApiResourceScopes"); migrationBuilder.DropTable( name: "ApiResourceSecrets"); migrationBuilder.DropTable( name: "ApiScopeClaims"); migrationBuilder.DropTable( name: "ApiScopeProperties"); migrationBuilder.DropTable( name: "ClientClaims"); migrationBuilder.DropTable( name: "ClientCorsOrigins"); migrationBuilder.DropTable( name: "ClientGrantTypes"); migrationBuilder.DropTable( name: "ClientIdPRestrictions"); migrationBuilder.DropTable( name: "ClientPostLogoutRedirectUris"); migrationBuilder.DropTable( name: "ClientProperties"); migrationBuilder.DropTable( name: "ClientRedirectUris"); migrationBuilder.DropTable( name: "ClientScopes"); migrationBuilder.DropTable( name: "ClientSecrets"); migrationBuilder.DropTable( name: "IdentityResourceClaims"); migrationBuilder.DropTable( name: "IdentityResourceProperties"); migrationBuilder.DropTable( name: "ApiResources"); migrationBuilder.DropTable( name: "ApiScopes"); migrationBuilder.DropTable( name: "Clients"); migrationBuilder.DropTable( name: "IdentityResources"); } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Data/Migrations/IdentityServer/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; namespace IdentityServer.Data.Migrations.IdentityServer.ConfigurationDb { [DbContext(typeof(ConfigurationDbContext))] partial class ConfigurationDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AllowedAccessTokenSigningAlgorithms") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Enabled") .HasColumnType("bit"); b.Property("LastAccessed") .HasColumnType("datetime2"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("NonEditable") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("ApiResources"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Scope") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(4000)") .HasMaxLength(4000); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceSecrets"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Emphasize") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Required") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("ApiScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ScopeId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ScopeId"); b.ToTable("ApiScopeClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("ScopeId") .HasColumnType("int"); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ScopeId"); b.ToTable("ApiScopeProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AbsoluteRefreshTokenLifetime") .HasColumnType("int"); b.Property("AccessTokenLifetime") .HasColumnType("int"); b.Property("AccessTokenType") .HasColumnType("int"); b.Property("AllowAccessTokensViaBrowser") .HasColumnType("bit"); b.Property("AllowOfflineAccess") .HasColumnType("bit"); b.Property("AllowPlainTextPkce") .HasColumnType("bit"); b.Property("AllowRememberConsent") .HasColumnType("bit"); b.Property("AllowedIdentityTokenSigningAlgorithms") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("AlwaysIncludeUserClaimsInIdToken") .HasColumnType("bit"); b.Property("AlwaysSendClientClaims") .HasColumnType("bit"); b.Property("AuthorizationCodeLifetime") .HasColumnType("int"); b.Property("BackChannelLogoutSessionRequired") .HasColumnType("bit"); b.Property("BackChannelLogoutUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("ClientClaimsPrefix") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("ConsentLifetime") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DeviceCodeLifetime") .HasColumnType("int"); b.Property("EnableLocalLogin") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("FrontChannelLogoutSessionRequired") .HasColumnType("bit"); b.Property("FrontChannelLogoutUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("IdentityTokenLifetime") .HasColumnType("int"); b.Property("IncludeJwtId") .HasColumnType("bit"); b.Property("LastAccessed") .HasColumnType("datetime2"); b.Property("LogoUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("NonEditable") .HasColumnType("bit"); b.Property("PairWiseSubjectSalt") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ProtocolType") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("RefreshTokenExpiration") .HasColumnType("int"); b.Property("RefreshTokenUsage") .HasColumnType("int"); b.Property("RequireClientSecret") .HasColumnType("bit"); b.Property("RequireConsent") .HasColumnType("bit"); b.Property("RequirePkce") .HasColumnType("bit"); b.Property("RequireRequestObject") .HasColumnType("bit"); b.Property("SlidingRefreshTokenLifetime") .HasColumnType("int"); b.Property("UpdateAccessTokenClaimsOnRefresh") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.Property("UserCodeType") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("UserSsoLifetime") .HasColumnType("int"); b.HasKey("Id"); b.HasIndex("ClientId") .IsUnique(); b.ToTable("Clients"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Origin") .IsRequired() .HasColumnType("nvarchar(150)") .HasMaxLength(150); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientCorsOrigins"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("GrantType") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientGrantTypes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Provider") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientIdPRestrictions"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("PostLogoutRedirectUri") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientPostLogoutRedirectUris"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("RedirectUri") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientRedirectUris"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Scope") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(4000)") .HasMaxLength(4000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientSecrets"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Emphasize") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("NonEditable") .HasColumnType("bit"); b.Property("Required") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("IdentityResources"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("IdentityResourceId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("IdentityResourceId"); b.ToTable("IdentityResourceClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("IdentityResourceId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("IdentityResourceId"); b.ToTable("IdentityResourceProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("UserClaims") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Properties") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Scopes") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Secrets") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") .WithMany("UserClaims") .HasForeignKey("ScopeId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") .WithMany("Properties") .HasForeignKey("ScopeId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("Claims") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedCorsOrigins") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedGrantTypes") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("IdentityProviderRestrictions") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("PostLogoutRedirectUris") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("Properties") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("RedirectUris") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedScopes") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("ClientSecrets") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") .WithMany("UserClaims") .HasForeignKey("IdentityResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") .WithMany("Properties") .HasForeignKey("IdentityResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); #pragma warning restore 612, 618 } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Data/Migrations/IdentityServer/PersistedGrantDb/20200625203357_InitialIdentityServerPersistedGrantDbMigration.Designer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; using Microsoft.EntityFrameworkCore.Migrations; namespace IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb { [DbContext(typeof(PersistedGrantDbContext))] [Migration("20200625203357_InitialIdentityServerPersistedGrantDbMigration")] partial class InitialIdentityServerPersistedGrantDbMigration { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() .HasColumnType("nvarchar(max)") .HasMaxLength(50000); b.Property("Description") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("DeviceCode") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); b.Property("SessionId") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("SubjectId") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("UserCode"); b.HasIndex("DeviceCode") .IsUnique(); b.HasIndex("Expiration"); b.ToTable("DeviceCodes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ConsumedTime") .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() .HasColumnType("nvarchar(max)") .HasMaxLength(50000); b.Property("Description") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("SessionId") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("SubjectId") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(50)") .HasMaxLength(50); b.HasKey("Key"); b.HasIndex("Expiration"); b.HasIndex("SubjectId", "ClientId", "Type"); b.HasIndex("SubjectId", "SessionId", "Type"); b.ToTable("PersistedGrants"); }); #pragma warning restore 612, 618 } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Data/Migrations/IdentityServer/PersistedGrantDb/20200625203357_InitialIdentityServerPersistedGrantDbMigration.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore.Migrations; namespace IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb { public partial class InitialIdentityServerPersistedGrantDbMigration : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "DeviceCodes", columns: table => new { UserCode = table.Column(maxLength: 200, nullable: false), DeviceCode = table.Column(maxLength: 200, nullable: false), SubjectId = table.Column(maxLength: 200, nullable: true), SessionId = table.Column(maxLength: 100, nullable: true), ClientId = table.Column(maxLength: 200, nullable: false), Description = table.Column(maxLength: 200, nullable: true), CreationTime = table.Column(nullable: false), Expiration = table.Column(nullable: false), Data = table.Column(maxLength: 50000, nullable: false) }, constraints: table => { table.PrimaryKey("PK_DeviceCodes", x => x.UserCode); }); migrationBuilder.CreateTable( name: "PersistedGrants", columns: table => new { Key = table.Column(maxLength: 200, nullable: false), Type = table.Column(maxLength: 50, nullable: false), SubjectId = table.Column(maxLength: 200, nullable: true), SessionId = table.Column(maxLength: 100, nullable: true), ClientId = table.Column(maxLength: 200, nullable: false), Description = table.Column(maxLength: 200, nullable: true), CreationTime = table.Column(nullable: false), Expiration = table.Column(nullable: true), ConsumedTime = table.Column(nullable: true), Data = table.Column(maxLength: 50000, nullable: false) }, constraints: table => { table.PrimaryKey("PK_PersistedGrants", x => x.Key); }); migrationBuilder.CreateIndex( name: "IX_DeviceCodes_DeviceCode", table: "DeviceCodes", column: "DeviceCode", unique: true); migrationBuilder.CreateIndex( name: "IX_DeviceCodes_Expiration", table: "DeviceCodes", column: "Expiration"); migrationBuilder.CreateIndex( name: "IX_PersistedGrants_Expiration", table: "PersistedGrants", column: "Expiration"); migrationBuilder.CreateIndex( name: "IX_PersistedGrants_SubjectId_ClientId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "ClientId", "Type" }); migrationBuilder.CreateIndex( name: "IX_PersistedGrants_SubjectId_SessionId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "SessionId", "Type" }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "DeviceCodes"); migrationBuilder.DropTable( name: "PersistedGrants"); } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Data/Migrations/IdentityServer/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Metadata; namespace IdentityServer.Data.Migrations.IdentityServer.PersistedGrantDb { [DbContext(typeof(PersistedGrantDbContext))] partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.5") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() .HasColumnType("nvarchar(max)") .HasMaxLength(50000); b.Property("Description") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("DeviceCode") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); b.Property("SessionId") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("SubjectId") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("UserCode"); b.HasIndex("DeviceCode") .IsUnique(); b.HasIndex("Expiration"); b.ToTable("DeviceCodes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ConsumedTime") .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() .HasColumnType("nvarchar(max)") .HasMaxLength(50000); b.Property("Description") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("SessionId") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("SubjectId") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(50)") .HasMaxLength(50); b.HasKey("Key"); b.HasIndex("Expiration"); b.HasIndex("SubjectId", "ClientId", "Type"); b.HasIndex("SubjectId", "SessionId", "Type"); b.ToTable("PersistedGrants"); }); #pragma warning restore 612, 618 } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.EntityFramework.DbContexts; global using IdentityServer8.EntityFramework.Mappers; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using IdentityServerHost.Quickstart.UI; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Hosting; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.EntityFrameworkCore; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Options; global using Microsoft.IdentityModel.Tokens; global using Serilog; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System; global using System.ComponentModel.DataAnnotations; global using System.Reflection; global using System.Security.Claims; global using System.Text; global using System.Text.Json; global using static IdentityModel.JwtClaimTypes; global using IdentityServerClaimValueTypes = IdentityServer8.IdentityServerConstants.ClaimValueTypes; ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/IdentityServer.csproj ================================================ ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ConfigureLogger(); try { Log.Information("Starting host..."); var builder = WebApplication.CreateBuilder(args); var services = builder.Services; services.AddControllersWithViews(); var migrationsAssembly = typeof(Program).GetTypeInfo().Assembly.GetName().Name; const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer8.Quickstart.EntityFramework-4.0.0;trusted_connection=yes;"; services.AddIdentityServer() .AddTestUsers(TestUsers.Users) .AddConfigurationStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); }) .AddOperationalStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); }) .AddDeveloperSigningCredential(); services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = ""; options.ClientSecret = ""; }) .AddOpenIdConnect("oidc", "Demo IdentityServer", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.SaveTokens = true; options.Authority = "https://demo.identityserver8.io/"; options.ClientId = "interactive.confidential"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.TokenValidationParameters = new() { NameClaimType = "name", RoleClaimType = "role" }; }); using (var app = builder.Build()) { // this will do the initial DB population InitializeDatabase(app); if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseStaticFiles() .UseRouting() .UseIdentityServer() .UseAuthorization(); app.MapDefaultControllerRoute(); await app.RunAsync(); } return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } void ConfigureLogger() => Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() // uncomment to write to Azure diagnostics stream //.WriteTo.File( // @"D:\home\LogFiles\Application\identityserver.txt", // fileSizeLimitBytes: 1_000_000, // rollOnFileSizeLimit: true, // shared: true, // flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) .CreateLogger(); void InitializeDatabase(IApplicationBuilder app) { using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) { serviceScope.ServiceProvider.GetRequiredService().Database.Migrate(); var context = serviceScope.ServiceProvider.GetRequiredService(); context.Database.Migrate(); if (!context.Clients.Any()) { foreach (var client in Config.Clients) { context.Clients.Add(client.ToEntity()); } context.SaveChanges(); } if (!context.IdentityResources.Any()) { foreach (var resource in Config.IdentityResources) { context.IdentityResources.Add(resource.ToEntity()); } context.SaveChanges(); } if (!context.ApiScopes.Any()) { foreach (var resource in Config.ApiScopes) { context.ApiScopes.Add(resource.ToEntity()); } context.SaveChanges(); } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Properties/launchSettings.json ================================================ { "profiles": { "SelfHost": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001" } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/AccountController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval /// [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController( IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; } /// /// Entry point into the login workflow /// [HttpGet] public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); if (vm.IsExternalLoginOnly) { // we only have one option for logging in and it's an external provider return returnUrl.IsAllowedRedirect() ? RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl = returnUrl.SanitizeForRedirect() }) : Forbid(); } return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (ModelState.IsValid) { // validate username/password against in-memory store if (_users.ValidateCredentials(model.Username, model.Password)) { var user = _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.Client.ClientId)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username }; await HttpContext.SignInAsync(isuser, props); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task LoginCancel(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else { // since we don't have a valid context, then we just go back to the home page return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } } /// /// Show logout page /// [HttpGet] public async Task Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); } /// /// Handle logout page postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await HttpContext.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); } [HttpGet] public IActionResult AccessDenied() { return View(); } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) { var local = context.IdP == IdentityServer8.IdentityServerConstants.LocalIdentityProvider; // this is meant to short circuit the UI and only trigger the one external IdP var vm = new LoginViewModel { EnableLocalLogin = local, ReturnUrl = returnUrl, Username = context?.LoginHint, }; if (!local) { vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; } return vm; } var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes .Where(x => x.DisplayName != null) .Select(x => new ExternalProvider { DisplayName = x.DisplayName ?? x.Name, AuthenticationScheme = x.Name }).ToList(); var allowLocal = true; if (context?.Client.ClientId != null) { var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); if (client != null) { allowLocal = client.EnableLocalLogin; if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) { providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); } } } return new LoginViewModel { AllowRememberLogin = AccountOptions.AllowRememberLogin, EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, ReturnUrl = returnUrl, Username = context?.LoginHint, ExternalProviders = providers.ToArray() }; } private async Task BuildLoginViewModelAsync(LoginInputModel model) { var vm = await BuildLoginViewModelAsync(model.ReturnUrl); vm.Username = model.Username; vm.RememberLogin = model.RememberLogin; return vm; } private async Task BuildLogoutViewModelAsync(string logoutId) { var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; if (User?.Identity.IsAuthenticated != true) { // if the user is not authenticated, then just show logged out page vm.ShowLogoutPrompt = false; return vm; } var context = await _interaction.GetLogoutContextAsync(logoutId); if (context?.ShowSignoutPrompt == false) { // it's safe to automatically sign-out vm.ShowLogoutPrompt = false; return vm; } // show the logout prompt. this prevents attacks where the user // is automatically signed out by another malicious web page. return vm; } private async Task BuildLoggedOutViewModelAsync(string logoutId) { // get context information (client name, post logout redirect URI and iframe for federated signout) var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LoggedOutViewModel { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; if (User?.Identity.IsAuthenticated == true) { var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; if (idp != null && idp != IdentityServer8.IdentityServerConstants.LocalIdentityProvider) { var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); if (providerSupportsSignout) { if (vm.LogoutId == null) { // if there's no current logout context, we need to create one // this captures necessary info from the current logged in user // before we signout and redirect away to the external IdP for signout vm.LogoutId = await _interaction.CreateLogoutContextAsync(); } vm.ExternalAuthenticationScheme = idp; } } } return vm; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/AccountOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI { public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/ExternalController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly ILogger _logger; private readonly IEventService _events; public ExternalController( IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService events, ILogger logger, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _logger = logger; _events = events; } /// /// initiate roundtrip to external authentication provider /// [HttpGet] public IActionResult Challenge(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; // validate returnUrl - either it is a valid OIDC URL or back to a local page if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } // start challenge and roundtrip the return URL and scheme var props = new AuthenticationProperties { RedirectUri = Url.Action(nameof(Callback)), Items = { { "returnUrl", returnUrl }, { "scheme", scheme }, } }; return Challenge(props, scheme); } /// /// Post processing of external authentication /// [HttpGet] public async Task Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = AutoProvisionUser(provider, providerUserId, claims); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); return (user, provider, providerUserId, claims); } private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) { var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); return user; } // if the external login is OIDC-based, there are certain things we need to preserve to make logout work // this will be different for WS-Fed, SAML2p or other protocols private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) { // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout var idToken = externalResult.Properties.GetTokenValue("id_token"); if (idToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/ExternalProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ExternalProvider { public string DisplayName { get; set; } public string AuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/LoggedOutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoggedOutViewModel { public string PostLogoutRedirectUri { get; set; } public string ClientName { get; set; } public string SignOutIframeUrl { get; set; } public bool AutomaticRedirectAfterSignOut { get; set; } public string LogoutId { get; set; } public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; public string ExternalAuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/LoginInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginInputModel { [Required] public string Username { get; set; } [Required] public string Password { get; set; } public bool RememberLogin { get; set; } public string ReturnUrl { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginViewModel : LoginInputModel { public bool AllowRememberLogin { get; set; } = true; public bool EnableLocalLogin { get; set; } = true; public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/LogoutInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutInputModel { public string LogoutId { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/LogoutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutViewModel : LogoutInputModel { public bool ShowLogoutPrompt { get; set; } = true; } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Account/RedirectViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class RedirectViewModel { public string RedirectUrl { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Consent/ConsentController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This controller processes the consent UI /// [SecurityHeaders] [Authorize] public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IEventService _events; private readonly ILogger _logger; public ConsentController( IIdentityServerInteractionService interaction, IEventService events, ILogger logger) { _interaction = interaction; _events = events; _logger = logger; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { var result = await ProcessConsent(model); if (result.IsRedirect) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context?.IsNativeClient() == true) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", result.RedirectUri); } return result.RedirectUri.IsAllowedRedirect() ? Redirect(result.RedirectUri.SanitizeForRedirect()) : Forbid(); } if (result.HasValidationError) { ModelState.AddModelError(string.Empty, result.ValidationError); } if (result.ShowView) { return View("Index", result.ViewModel); } return View("Error"); } /*****************************************/ /* helper APIs for the ConsentController */ /*****************************************/ private async Task ProcessConsent(ConsentInputModel model) { var result = new ProcessConsentResult(); // validate return url is still valid var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model?.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model?.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); } return result; } private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { return CreateConsentViewModel(model, returnUrl, request); } else { _logger.LogError("No consent request matching request: {0}", returnUrl.SanitizeForLog()); } return null; } private ConsentViewModel CreateConsentViewModel( ConsentInputModel model, string returnUrl, AuthorizationRequest request) { var vm = new ConsentViewModel { RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), Description = model?.Description, ReturnUrl = returnUrl, ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach(var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { var displayName = apiScope.DisplayName ?? apiScope.Name; if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) { displayName += ":" + parsedScopeValue.ParsedParameter; } return new ScopeViewModel { Value = parsedScopeValue.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Consent/ConsentInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentInputModel { public string Button { get; set; } public IEnumerable ScopesConsented { get; set; } public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Consent/ConsentOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentOptions { public static bool EnableOfflineAccess = true; public static string OfflineAccessDisplayName = "Offline Access"; public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Consent/ConsentViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentViewModel : ConsentInputModel { public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public bool AllowRememberConsent { get; set; } public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Consent/ProcessConsentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ProcessConsentResult { public bool IsRedirect => RedirectUri != null; public string RedirectUri { get; set; } public Client Client { get; set; } public bool ShowView => ViewModel != null; public ConsentViewModel ViewModel { get; set; } public bool HasValidationError => ValidationError != null; public string ValidationError { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Consent/ScopeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ScopeViewModel { public string Value { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Emphasize { get; set; } public bool Required { get; set; } public bool Checked { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationViewModel : ConsentViewModel { public string UserCode { get; set; } public bool ConfirmUserCode { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Device/DeviceController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [Authorize] [SecurityHeaders] public class DeviceController : Controller { private readonly IDeviceFlowInteractionService _interaction; private readonly IEventService _events; private readonly IOptions _options; private readonly ILogger _logger; public DeviceController( IDeviceFlowInteractionService interaction, IEventService eventService, IOptions options, ILogger logger) { _interaction = interaction; _events = eventService; _options = options; _logger = logger; } [HttpGet] public async Task Index() { string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; string userCode = Request.Query[userCodeParamName]; if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); vm.ConfirmUserCode = true; return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task UserCodeCapture(string userCode) { var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task Callback(DeviceAuthorizationInputModel model) { if (model == null) throw new ArgumentNullException(nameof(model)); var result = await ProcessConsent(model); if (result.HasValidationError) return View("Error"); return View("Success"); } private async Task ProcessConsent(DeviceAuthorizationInputModel model) { var result = new ProcessConsentResult(); var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.UserCode, model); } return result; } private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(userCode); if (request != null) { return CreateConsentViewModel(userCode, model, request); } return null; } private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { UserCode = userCode, Description = model?.Description, RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? DisplayName = apiScope.DisplayName ?? apiScope.Name, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [Authorize] public class DiagnosticsController : Controller { public async Task Index() { var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) { return NotFound(); } var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); return View(model); } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DiagnosticsViewModel { public DiagnosticsViewModel(AuthenticateResult result) { AuthenticateResult = result; if (result.Properties.Items.ContainsKey("client_list")) { var encoded = result.Properties.Items["client_list"]; var bytes = Base64Url.Decode(encoded); var value = Encoding.UTF8.GetString(bytes); Clients = JsonSerializer.Deserialize(value); } } public AuthenticateResult AuthenticateResult { get; } public IEnumerable Clients { get; } = new List(); } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public static class Extensions { /// /// Checks if the redirect URI is for a native client. /// /// public static bool IsNativeClient(this AuthorizationRequest context) { return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); } public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) { controller.HttpContext.Response.StatusCode = 200; controller.HttpContext.Response.Headers["Location"] = ""; return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Grants/GrantsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller allows a user to revoke grants given to clients /// [SecurityHeaders] [Authorize] public class GrantsController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; private readonly IResourceStore _resources; private readonly IEventService _events; public GrantsController(IIdentityServerInteractionService interaction, IClientStore clients, IResourceStore resources, IEventService events) { _interaction = interaction; _clients = clients; _resources = resources; _events = events; } /// /// Show list of grants /// [HttpGet] public async Task Index() { return View("Index", await BuildViewModelAsync()); } /// /// Handle postback to revoke a client /// [HttpPost] [ValidateAntiForgeryToken] public async Task Revoke(string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return RedirectToAction("Index"); } private async Task BuildViewModelAsync() { var grants = await _interaction.GetAllUserGrantsAsync(); var list = new List(); foreach(var grant in grants) { var client = await _clients.FindClientByIdAsync(grant.ClientId); if (client != null) { var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); var item = new GrantViewModel() { ClientId = client.ClientId, ClientName = client.ClientName ?? client.ClientId, ClientLogoUrl = client.LogoUri, ClientUrl = client.ClientUri, Description = grant.Description, Created = grant.CreationTime, Expires = grant.Expiration, IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() }; list.Add(item); } } return new GrantsViewModel { Grants = list }; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Grants/GrantsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class GrantsViewModel { public IEnumerable Grants { get; set; } } public class GrantViewModel { public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public string Description { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Home/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ErrorViewModel { public ErrorViewModel() { } public ErrorViewModel(string error) { Error = new ErrorMessage { Error = error }; } public ErrorMessage Error { get; set; } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/Home/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) { _interaction = interaction; _environment = environment; _logger = logger; } public IActionResult Index() { if (_environment.IsDevelopment()) { // only show in development return View(); } _logger.LogInformation("Homepage is disabled in production. Returning 404."); return NotFound(); } /// /// Shows the error page /// public async Task Error(string errorId) { var vm = new ErrorViewModel(); // retrieve error details from identityserver var message = await _interaction.GetErrorContextAsync(errorId); if (message != null) { vm.Error = message; if (!_environment.IsDevelopment()) { // only show in development message.ErrorDescription = null; } } return View("Error", vm); } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/SecurityHeadersAttribute.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class SecurityHeadersAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { var result = context.Result; if (result is ViewResult) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) { context.HttpContext.Response.Headers.Append("X-Content-Type-Options", "nosniff"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) { context.HttpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; // also consider adding upgrade-insecure-requests once you have HTTPS in place for production //csp += "upgrade-insecure-requests;"; // also an example if you need client images to be displayed from twitter // csp += "img-src 'self' https://pbs.twimg.com;"; // once for standards compliant browsers if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("Content-Security-Policy", csp); } // and once again for IE if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("X-Content-Security-Policy", csp); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy var referrer_policy = "no-referrer"; if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) { context.HttpContext.Response.Headers.Append("Referrer-Policy", referrer_policy); } } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Quickstart/TestUsers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class TestUsers { static readonly object UserAddress = new { street_address = "One Hacker Way", locality = "Heidelberg", postal_code = 69118, country = "Germany" }; public static List Users => new List { new() { SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new (Name, "Alice Smith"), new (GivenName, "Alice"), new (FamilyName, "Smith"), new (Email, "AliceSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://alice.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } }, new() { SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new (Name, "Bob Smith"), new (GivenName, "Bob"), new (FamilyName, "Smith"), new (Email, "BobSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://bob.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } } }; } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8; using IdentityServer8.EntityFramework.DbContexts; using IdentityServer8.EntityFramework.Mappers; using IdentityServerHost.Quickstart.UI; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Hosting; using Microsoft.IdentityModel.Tokens; using System.Linq; using System.Reflection; namespace IdentityServer { public class Startup { public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; const string connectionString = @"Data Source=(LocalDb)\MSSQLLocalDB;database=IdentityServer8.Quickstart.EntityFramework-4.0.0;trusted_connection=yes;"; var builder = services.AddIdentityServer() .AddTestUsers(TestUsers.Users) .AddConfigurationStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); }) .AddOperationalStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(connectionString, sql => sql.MigrationsAssembly(migrationsAssembly)); }); builder.AddDeveloperSigningCredential(); services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = ""; options.ClientSecret = ""; }) .AddOpenIdConnect("oidc", "Demo IdentityServer", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.SaveTokens = true; options.Authority = "https://demo.identityserver8.io/"; options.ClientId = "interactive.confidential"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { // this will do the initial DB population InitializeDatabase(app); if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); } app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } private void InitializeDatabase(IApplicationBuilder app) { using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) { serviceScope.ServiceProvider.GetRequiredService().Database.Migrate(); var context = serviceScope.ServiceProvider.GetRequiredService(); context.Database.Migrate(); if (!context.Clients.Any()) { foreach (var client in Config.Clients) { context.Clients.Add(client.ToEntity()); } context.SaveChanges(); } if (!context.IdentityResources.Any()) { foreach (var resource in Config.IdentityResources) { context.IdentityResources.Add(resource.ToEntity()); } context.SaveChanges(); } if (!context.ApiScopes.Any()) { foreach (var resource in Config.ApiScopes) { context.ApiScopes.Add(resource.ToEntity()); } context.SaveChanges(); } } } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Account/AccessDenied.cshtml ================================================ 

    Access Denied

    You do not have access to that resource.

    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Account/LoggedOut.cshtml ================================================ @model LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user ViewData["signed-out"] = true; }

    Logout You are now logged out

    @if (Model.PostLogoutRedirectUri != null) {
    Click here to return to the @Model.ClientName application.
    } @if (Model.SignOutIframeUrl != null) { }
    @section scripts { @if (Model.AutomaticRedirectAfterSignOut) { } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Account/Login.cshtml ================================================ @model LoginViewModel ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Account/Logout.cshtml ================================================ @model LogoutViewModel

    Logout

    Would you like to logout of IdentityServer?

    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Device/Success.cshtml ================================================

    Success

    You have successfully authorized the device

    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Device/UserCodeCapture.cshtml ================================================ @model string

    User Code

    Please enter the code displayed on your device.

    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
    @if (Model.ClientLogoUrl != null) { }

    @Model.ClientName is requesting your permission

    @if (Model.ConfirmUserCode) {

    Please confirm that the authorization request quotes the code: @Model.UserCode.

    }

    Uncheck the permissions you do not wish to grant.

    @if (Model.IdentityScopes.Any()) {
    Personal Information
      @foreach (var scope in Model.IdentityScopes) { }
    } @if (Model.ApiScopes.Any()) {
    Application Access
      @foreach (var scope in Model.ApiScopes) { }
    }
    Description
    @if (Model.AllowRememberConsent) {
    }
    @if (Model.ClientUrl != null) { @Model.ClientName }
    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

    Authentication Cookie

    Claims

    @foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in Model.AuthenticateResult.Properties.Items) {
    @prop.Key
    @prop.Value
    } @if (Model.Clients.Any()) {
    Clients
    @{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
    }
    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

    Client Application Permissions

    Below is the list of applications you have given permission to and the resources they have access to.

    @if (Model.Grants.Any() == false) {
    You have not given access to any applications
    } else { foreach (var grant in Model.Grants) {
    @if (grant.ClientLogoUrl != null) { } @grant.ClientName
      @if (grant.Description != null) {
    • @grant.Description
    • }
    • @grant.Created.ToString("yyyy-MM-dd")
    • @if (grant.Expires.HasValue) {
    • @grant.Expires.Value.ToString("yyyy-MM-dd")
    • } @if (grant.IdentityGrantNames.Any()) {
      • @foreach (var name in grant.IdentityGrantNames) {
      • @name
      • }
    • } @if (grant.ApiGrantNames.Any()) {
      • @foreach (var name in grant.ApiGrantNames) {
      • @name
      • }
    • }
    } }
    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @{ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); }

    Welcome to IdentityServer8 (version @version)

    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

    Error

    Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
    @errorDescription
    } }
    @if (request_id != null) {
    Request Id: @request_id
    }
    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

    You are now being returned to the application

    Once complete, you may close this tab.

    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
    @RenderBody()
    @RenderSection("scripts", required: false) ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/wwwroot/js/signin-redirect.js ================================================ // window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/IdentityServer/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Controllers/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class HomeController : Controller { private readonly ILogger _logger; public HomeController(ILogger logger) { _logger = logger; } public IActionResult Index() { return View(); } public async Task CallApi() { var accessToken = await HttpContext.GetTokenAsync("access_token"); var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var content = await client.GetStringAsync("https://localhost:6001/identity"); var obj = JsonSerializer.Deserialize(content); ViewBag.Json = obj.ToString(); return View("json"); } public IActionResult Logout() { return SignOut("Cookies", "oidc"); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Mvc; global using System.Diagnostics; global using System.IdentityModel.Tokens.Jwt; global using System.Net.Http.Headers; global using System.Text.Json; ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Models/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class ErrorViewModel { public string RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/MvcClient.csproj ================================================ true PreserveNewest ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ JwtSecurityTokenHandler.DefaultMapInboundClaims = false; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); builder.Services .AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://localhost:5001"; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.Scope.Add("api1"); options.SaveTokens = true; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); else app.UseExceptionHandler("/Home/Error"); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapDefaultControllerRoute().RequireAuthorization(); await app.RunAsync(); } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Properties/launchSettings.json ================================================ { "profiles": { "MvcClient": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5002" } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Views/Home/Index.cshtml ================================================ @using Microsoft.AspNetCore.Authentication

    Claims

    @foreach (var claim in User.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items) {
    @prop.Key
    @prop.Value
    }
    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Views/Home/Privacy.cshtml ================================================ @{ ViewData["Title"] = "Privacy Policy"; }

    @ViewData["Title"]

    Use this page to detail your site's privacy policy.

    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ ViewData["Title"] = "Error"; }

    Error.

    An error occurred while processing your request.

    @if (Model.ShowRequestId) {

    Request ID: @Model.RequestId

    }

    Development Mode

    Swapping to Development environment will display more detailed information about the error that occurred.

    The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app.

    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Views/Shared/_Layout.cshtml ================================================ @ViewData["Title"] - MvcClient
    @RenderBody()
    @RenderSection("Scripts", required: false) ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Views/Shared/_ValidationScriptsPartial.cshtml ================================================  ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Views/Shared/json.cshtml ================================================
    @ViewBag.Json
    ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Views/_ViewImports.cshtml ================================================ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/appsettings.json ================================================ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/wwwroot/css/site.css ================================================ /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification for details on configuring this project to bundle and minify static web assets. */ a.navbar-brand { white-space: normal; text-align: center; word-break: break-all; } /* Provide sufficient contrast against white background */ a { color: #0366d6; } .btn-primary { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } /* Sticky footer styles -------------------------------------------------- */ html { font-size: 14px; } @media (min-width: 768px) { html { font-size: 16px; } } .border-top { border-top: 1px solid #e5e5e5; } .border-bottom { border-bottom: 1px solid #e5e5e5; } .box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); } button.accept-policy { font-size: 1rem; line-height: inherit; } /* Sticky footer styles -------------------------------------------------- */ html { position: relative; min-height: 100%; } body { /* Margin bottom by footer height */ margin-bottom: 60px; } .footer { position: absolute; bottom: 0; width: 100%; white-space: nowrap; line-height: 60px; /* Vertically center the text there */ } ================================================ FILE: samples/Quickstarts/5_EntityFramework/src/MvcClient/wwwroot/js/site.js ================================================ // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification // for details on configuring this project to bundle and minify static web assets. // Write your JavaScript code. ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/Quickstart.sln ================================================  Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34322.80 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{BD8BA25C-A91D-419D-99B6-B575ADD6D061}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MvcClient", "src\MvcClient\MvcClient.csproj", "{BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServerAspNetIdentity", "src\IdentityServerAspNetIdentity\IdentityServerAspNetIdentity.csproj", "{C49859B4-B34C-4594-A307-674719F71122}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Api", "..\Shared\src\Api\Api.csproj", "{BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Client", "..\Shared\src\Client\Client.csproj", "{3BFAC242-776F-4B31-9658-D48F05BF355D}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|Any CPU.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x64.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x64.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x86.ActiveCfg = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Debug|x86.Build.0 = Debug|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|Any CPU.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|Any CPU.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x64.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x64.Build.0 = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x86.ActiveCfg = Release|Any CPU {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12}.Release|x86.Build.0 = Release|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Debug|Any CPU.Build.0 = Debug|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Debug|x64.ActiveCfg = Debug|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Debug|x64.Build.0 = Debug|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Debug|x86.ActiveCfg = Debug|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Debug|x86.Build.0 = Debug|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Release|Any CPU.ActiveCfg = Release|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Release|Any CPU.Build.0 = Release|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Release|x64.ActiveCfg = Release|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Release|x64.Build.0 = Release|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Release|x86.ActiveCfg = Release|Any CPU {C49859B4-B34C-4594-A307-674719F71122}.Release|x86.Build.0 = Release|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Debug|Any CPU.Build.0 = Debug|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Debug|x64.ActiveCfg = Debug|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Debug|x64.Build.0 = Debug|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Debug|x86.ActiveCfg = Debug|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Debug|x86.Build.0 = Debug|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Release|Any CPU.ActiveCfg = Release|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Release|Any CPU.Build.0 = Release|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Release|x64.ActiveCfg = Release|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Release|x64.Build.0 = Release|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Release|x86.ActiveCfg = Release|Any CPU {BC4283E1-A6F7-43BF-B0E4-8D91531DD0BE}.Release|x86.Build.0 = Release|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Debug|Any CPU.Build.0 = Debug|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Debug|x64.ActiveCfg = Debug|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Debug|x64.Build.0 = Debug|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Debug|x86.ActiveCfg = Debug|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Debug|x86.Build.0 = Debug|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Release|Any CPU.ActiveCfg = Release|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Release|Any CPU.Build.0 = Release|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Release|x64.ActiveCfg = Release|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Release|x64.Build.0 = Release|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Release|x86.ActiveCfg = Release|Any CPU {3BFAC242-776F-4B31-9658-D48F05BF355D}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {BF5923FF-9E93-4C01-93C2-8CE7A3F62D12} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} {C49859B4-B34C-4594-A307-674719F71122} = {BD8BA25C-A91D-419D-99B6-B575ADD6D061} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {84D5AF06-1243-46F1-9D58-70ED572832C3} EndGlobalSection EndGlobal ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/Quickstart.sln.licenseheader ================================================ extensions: designer.cs generated.cs extensions: .cs /* Copyright (c) 2024 HigginsSoft Written by Alexander Higgins https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code for this software can be found at https://github.com/alexhiggins732/IdentityServer8 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Config.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public static class Config { public static IEnumerable IdentityResources => new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable ApiScopes => new List { new ApiScope("api1", "My API") }; public static IEnumerable Clients => new List { // machine to machine client new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, // scopes that client has access to AllowedScopes = { "api1" } }, // interactive ASP.NET Core MVC client new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // where to redirect to after login RedirectUris = { "https://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } } }; } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Data/ApplicationDbContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.Identity.EntityFrameworkCore; namespace IdentityServerAspNetIdentity.Data { public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // Customize the ASP.NET Identity model and override the defaults if needed. // For example, you can rename the ASP.NET Identity table names and more. // Add your customizations after calling base.OnModelCreating(builder); } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Data/Migrations/20180109192453_CreateIdentitySchema.Designer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // using Microsoft.EntityFrameworkCore.Infrastructure; using Microsoft.EntityFrameworkCore.Migrations; namespace IdentityServerAspNetIdentity.Data.Migrations { [DbContext(typeof(ApplicationDbContext))] [Migration("20180109192453_CreateIdentitySchema")] partial class CreateIdentitySchema { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); modelBuilder.Entity("IdentityServerAspNetIdentity.Models.ApplicationUser", b => { b.Property("Id") .ValueGeneratedOnAdd(); b.Property("AccessFailedCount"); b.Property("ConcurrencyStamp") .IsConcurrencyToken(); b.Property("Email") .HasMaxLength(256); b.Property("EmailConfirmed"); b.Property("LockoutEnabled"); b.Property("LockoutEnd"); b.Property("NormalizedEmail") .HasMaxLength(256); b.Property("NormalizedUserName") .HasMaxLength(256); b.Property("PasswordHash"); b.Property("PhoneNumber"); b.Property("PhoneNumberConfirmed"); b.Property("SecurityStamp"); b.Property("TwoFactorEnabled"); b.Property("UserName") .HasMaxLength(256); b.HasKey("Id"); b.HasIndex("NormalizedEmail") .HasName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() .HasName("UserNameIndex"); b.ToTable("AspNetUsers"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { b.Property("Id") .ValueGeneratedOnAdd(); b.Property("ConcurrencyStamp") .IsConcurrencyToken(); b.Property("Name") .HasMaxLength(256); b.Property("NormalizedName") .HasMaxLength(256); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() .HasName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") .ValueGeneratedOnAdd(); b.Property("ClaimType"); b.Property("ClaimValue"); b.Property("RoleId") .IsRequired(); b.HasKey("Id"); b.HasIndex("RoleId"); b.ToTable("AspNetRoleClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.Property("Id") .ValueGeneratedOnAdd(); b.Property("ClaimType"); b.Property("ClaimValue"); b.Property("UserId") .IsRequired(); b.HasKey("Id"); b.HasIndex("UserId"); b.ToTable("AspNetUserClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider"); b.Property("ProviderKey"); b.Property("ProviderDisplayName"); b.Property("UserId") .IsRequired(); b.HasKey("LoginProvider", "ProviderKey"); b.HasIndex("UserId"); b.ToTable("AspNetUserLogins"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.Property("UserId"); b.Property("RoleId"); b.HasKey("UserId", "RoleId"); b.HasIndex("RoleId"); b.ToTable("AspNetUserRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.Property("UserId"); b.Property("LoginProvider"); b.Property("Name"); b.Property("Value"); b.HasKey("UserId", "LoginProvider", "Name"); b.ToTable("AspNetUserTokens"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade); b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); #pragma warning restore 612, 618 } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Data/Migrations/20180109192453_CreateIdentitySchema.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore.Migrations; namespace IdentityServerAspNetIdentity.Data.Migrations { public partial class CreateIdentitySchema : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "AspNetRoles", columns: table => new { Id = table.Column(nullable: false), ConcurrencyStamp = table.Column(nullable: true), Name = table.Column(maxLength: 256, nullable: true), NormalizedName = table.Column(maxLength: 256, nullable: true) }, constraints: table => { table.PrimaryKey("PK_AspNetRoles", x => x.Id); }); migrationBuilder.CreateTable( name: "AspNetUsers", columns: table => new { Id = table.Column(nullable: false), AccessFailedCount = table.Column(nullable: false), ConcurrencyStamp = table.Column(nullable: true), Email = table.Column(maxLength: 256, nullable: true), EmailConfirmed = table.Column(nullable: false), LockoutEnabled = table.Column(nullable: false), LockoutEnd = table.Column(nullable: true), NormalizedEmail = table.Column(maxLength: 256, nullable: true), NormalizedUserName = table.Column(maxLength: 256, nullable: true), PasswordHash = table.Column(nullable: true), PhoneNumber = table.Column(nullable: true), PhoneNumberConfirmed = table.Column(nullable: false), SecurityStamp = table.Column(nullable: true), TwoFactorEnabled = table.Column(nullable: false), UserName = table.Column(maxLength: 256, nullable: true) }, constraints: table => { table.PrimaryKey("PK_AspNetUsers", x => x.Id); }); migrationBuilder.CreateTable( name: "AspNetRoleClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("Sqlite:Autoincrement", true), ClaimType = table.Column(nullable: true), ClaimValue = table.Column(nullable: true), RoleId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); table.ForeignKey( name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", column: x => x.RoleId, principalTable: "AspNetRoles", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "AspNetUserClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("Sqlite:Autoincrement", true), ClaimType = table.Column(nullable: true), ClaimValue = table.Column(nullable: true), UserId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); table.ForeignKey( name: "FK_AspNetUserClaims_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "AspNetUserLogins", columns: table => new { LoginProvider = table.Column(nullable: false), ProviderKey = table.Column(nullable: false), ProviderDisplayName = table.Column(nullable: true), UserId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); table.ForeignKey( name: "FK_AspNetUserLogins_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "AspNetUserRoles", columns: table => new { UserId = table.Column(nullable: false), RoleId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); table.ForeignKey( name: "FK_AspNetUserRoles_AspNetRoles_RoleId", column: x => x.RoleId, principalTable: "AspNetRoles", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_AspNetUserRoles_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "AspNetUserTokens", columns: table => new { UserId = table.Column(nullable: false), LoginProvider = table.Column(nullable: false), Name = table.Column(nullable: false), Value = table.Column(nullable: true) }, constraints: table => { table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); table.ForeignKey( name: "FK_AspNetUserTokens_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( name: "IX_AspNetRoleClaims_RoleId", table: "AspNetRoleClaims", column: "RoleId"); migrationBuilder.CreateIndex( name: "RoleNameIndex", table: "AspNetRoles", column: "NormalizedName", unique: true); migrationBuilder.CreateIndex( name: "IX_AspNetUserClaims_UserId", table: "AspNetUserClaims", column: "UserId"); migrationBuilder.CreateIndex( name: "IX_AspNetUserLogins_UserId", table: "AspNetUserLogins", column: "UserId"); migrationBuilder.CreateIndex( name: "IX_AspNetUserRoles_RoleId", table: "AspNetUserRoles", column: "RoleId"); migrationBuilder.CreateIndex( name: "EmailIndex", table: "AspNetUsers", column: "NormalizedEmail"); migrationBuilder.CreateIndex( name: "UserNameIndex", table: "AspNetUsers", column: "NormalizedUserName", unique: true); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "AspNetRoleClaims"); migrationBuilder.DropTable( name: "AspNetUserClaims"); migrationBuilder.DropTable( name: "AspNetUserLogins"); migrationBuilder.DropTable( name: "AspNetUserRoles"); migrationBuilder.DropTable( name: "AspNetUserTokens"); migrationBuilder.DropTable( name: "AspNetRoles"); migrationBuilder.DropTable( name: "AspNetUsers"); } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Data/Migrations/ApplicationDbContextModelSnapshot.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // using Microsoft.EntityFrameworkCore.Infrastructure; namespace IdentityServerAspNetIdentity.Data.Migrations { [DbContext(typeof(ApplicationDbContext))] partial class ApplicationDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "2.0.1-rtm-125"); modelBuilder.Entity("IdentityServerAspNetIdentity.Models.ApplicationUser", b => { b.Property("Id") .ValueGeneratedOnAdd(); b.Property("AccessFailedCount"); b.Property("ConcurrencyStamp") .IsConcurrencyToken(); b.Property("Email") .HasMaxLength(256); b.Property("EmailConfirmed"); b.Property("LockoutEnabled"); b.Property("LockoutEnd"); b.Property("NormalizedEmail") .HasMaxLength(256); b.Property("NormalizedUserName") .HasMaxLength(256); b.Property("PasswordHash"); b.Property("PhoneNumber"); b.Property("PhoneNumberConfirmed"); b.Property("SecurityStamp"); b.Property("TwoFactorEnabled"); b.Property("UserName") .HasMaxLength(256); b.HasKey("Id"); b.HasIndex("NormalizedEmail") .HasName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() .HasName("UserNameIndex"); b.ToTable("AspNetUsers"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { b.Property("Id") .ValueGeneratedOnAdd(); b.Property("ConcurrencyStamp") .IsConcurrencyToken(); b.Property("Name") .HasMaxLength(256); b.Property("NormalizedName") .HasMaxLength(256); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() .HasName("RoleNameIndex"); b.ToTable("AspNetRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") .ValueGeneratedOnAdd(); b.Property("ClaimType"); b.Property("ClaimValue"); b.Property("RoleId") .IsRequired(); b.HasKey("Id"); b.HasIndex("RoleId"); b.ToTable("AspNetRoleClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.Property("Id") .ValueGeneratedOnAdd(); b.Property("ClaimType"); b.Property("ClaimValue"); b.Property("UserId") .IsRequired(); b.HasKey("Id"); b.HasIndex("UserId"); b.ToTable("AspNetUserClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider"); b.Property("ProviderKey"); b.Property("ProviderDisplayName"); b.Property("UserId") .IsRequired(); b.HasKey("LoginProvider", "ProviderKey"); b.HasIndex("UserId"); b.ToTable("AspNetUserLogins"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.Property("UserId"); b.Property("RoleId"); b.HasKey("UserId", "RoleId"); b.HasIndex("RoleId"); b.ToTable("AspNetUserRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.Property("UserId"); b.Property("LoginProvider"); b.Property("Name"); b.Property("Value"); b.HasKey("UserId", "LoginProvider", "Name"); b.ToTable("AspNetUserTokens"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole") .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade); b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.HasOne("IdentityServerAspNetIdentity.Models.ApplicationUser") .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade); }); #pragma warning restore 612, 618 } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using IdentityServerAspNetIdentity; global using IdentityServerHost.Quickstart.UI; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Hosting; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Options; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.IdentityModel.Tokens; global using Serilog; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System; global using System.ComponentModel.DataAnnotations; global using System.Linq; global using System.Security.Claims; global using System.Text; global using System.Text.Json; global using static IdentityModel.JwtClaimTypes; global using IdentityServerClaimValueTypes = IdentityServer8.IdentityServerConstants.ClaimValueTypes; global using IdentityServerAspNetIdentity.Data; global using IdentityServerAspNetIdentity.Models; global using Microsoft.AspNetCore.Identity; global using Microsoft.EntityFrameworkCore; global using Secret = IdentityServer8.Models.Secret; ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/IdentityServerAspNetIdentity.csproj ================================================ all runtime; build; native; contentfiles; analyzers; buildtransitive ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Models/ApplicationUser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.Identity; namespace IdentityServerAspNetIdentity.Models { // Add profile data for application users by adding properties to the ApplicationUser class public class ApplicationUser : IdentityUser { } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ConfigureLogger(); try { var builder = WebApplication.CreateBuilder(args.Where(x => x != "/seed").ToArray()); var services = builder.Services; services.AddControllersWithViews(); services.AddDbContext(options => options.UseSqlite(builder.Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); services.AddIdentityServer(options => { options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseSuccessEvents = true; // see https://IdentityServer8.readthedocs.io/en/latest/topics/resources.html options.EmitStaticAudienceClaim = true; }) .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddAspNetIdentity() // not recommended for production - you need to store your key material somewhere secure .AddDeveloperSigningCredential(); services.AddAuthentication() .AddGoogle(options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; // register your IdentityServer with Google at https://console.developers.google.com // enable the Google+ API // set the redirect URI to https://localhost:5001/signin-google options.ClientId = "copy client ID from Google here"; options.ClientSecret = "copy client secret from Google here"; }); using (var app = builder.Build()) { if (args.Contains("/seed")) { Log.Information("Seeding database..."); var config = app.Services.GetRequiredService(); var connectionString = config.GetConnectionString("DefaultConnection"); SeedData.EnsureSeedData(connectionString); Log.Information("Done seeding database."); return 0; } if (app.Environment.IsDevelopment()) { app.UseDeveloperExceptionPage(); app.UseMigrationsEndPoint(); app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Home/Error"); app.UseHsts(); } app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); app.MapDefaultControllerRoute(); Log.Information("Starting host..."); await app.RunAsync(); } return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } void ConfigureLogger() => Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() // uncomment to write to Azure diagnostics stream //.WriteTo.File( // @"D:\home\LogFiles\Application\identityserver.txt", // fileSizeLimitBytes: 1_000_000, // rollOnFileSizeLimit: true, // shared: true, // flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) .CreateLogger(); ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Properties/launchSettings.json ================================================ { "profiles": { "SelfHost": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001" } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/AccountController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval /// [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController( IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; } /// /// Entry point into the login workflow /// [HttpGet] public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); if (vm.IsExternalLoginOnly) { // we only have one option for logging in and it's an external provider return returnUrl.IsAllowedRedirect() ? RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl = returnUrl.SanitizeForRedirect() }) : Forbid(); } return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (ModelState.IsValid) { // validate username/password against in-memory store if (_users.ValidateCredentials(model.Username, model.Password)) { var user = _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.Client.ClientId)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username }; await HttpContext.SignInAsync(isuser, props); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task LoginCancel(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else { // since we don't have a valid context, then we just go back to the home page return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } } /// /// Show logout page /// [HttpGet] public async Task Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); } /// /// Handle logout page postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await HttpContext.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); } [HttpGet] public IActionResult AccessDenied() { return View(); } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) { var local = context.IdP == IdentityServer8.IdentityServerConstants.LocalIdentityProvider; // this is meant to short circuit the UI and only trigger the one external IdP var vm = new LoginViewModel { EnableLocalLogin = local, ReturnUrl = returnUrl, Username = context?.LoginHint, }; if (!local) { vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; } return vm; } var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes .Where(x => x.DisplayName != null) .Select(x => new ExternalProvider { DisplayName = x.DisplayName ?? x.Name, AuthenticationScheme = x.Name }).ToList(); var allowLocal = true; if (context?.Client.ClientId != null) { var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); if (client != null) { allowLocal = client.EnableLocalLogin; if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) { providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); } } } return new LoginViewModel { AllowRememberLogin = AccountOptions.AllowRememberLogin, EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, ReturnUrl = returnUrl, Username = context?.LoginHint, ExternalProviders = providers.ToArray() }; } private async Task BuildLoginViewModelAsync(LoginInputModel model) { var vm = await BuildLoginViewModelAsync(model.ReturnUrl); vm.Username = model.Username; vm.RememberLogin = model.RememberLogin; return vm; } private async Task BuildLogoutViewModelAsync(string logoutId) { var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; if (User?.Identity.IsAuthenticated != true) { // if the user is not authenticated, then just show logged out page vm.ShowLogoutPrompt = false; return vm; } var context = await _interaction.GetLogoutContextAsync(logoutId); if (context?.ShowSignoutPrompt == false) { // it's safe to automatically sign-out vm.ShowLogoutPrompt = false; return vm; } // show the logout prompt. this prevents attacks where the user // is automatically signed out by another malicious web page. return vm; } private async Task BuildLoggedOutViewModelAsync(string logoutId) { // get context information (client name, post logout redirect URI and iframe for federated signout) var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LoggedOutViewModel { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; if (User?.Identity.IsAuthenticated == true) { var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; if (idp != null && idp != IdentityServer8.IdentityServerConstants.LocalIdentityProvider) { var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); if (providerSupportsSignout) { if (vm.LogoutId == null) { // if there's no current logout context, we need to create one // this captures necessary info from the current logged in user // before we signout and redirect away to the external IdP for signout vm.LogoutId = await _interaction.CreateLogoutContextAsync(); } vm.ExternalAuthenticationScheme = idp; } } } return vm; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/AccountOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI { public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/ExternalController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly ILogger _logger; private readonly IEventService _events; public ExternalController( IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService events, ILogger logger, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _logger = logger; _events = events; } /// /// initiate roundtrip to external authentication provider /// [HttpGet] public IActionResult Challenge(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; // validate returnUrl - either it is a valid OIDC URL or back to a local page if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } // start challenge and roundtrip the return URL and scheme var props = new AuthenticationProperties { RedirectUri = Url.Action(nameof(Callback)), Items = { { "returnUrl", returnUrl }, { "scheme", scheme }, } }; return Challenge(props, scheme); } /// /// Post processing of external authentication /// [HttpGet] public async Task Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = AutoProvisionUser(provider, providerUserId, claims); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); return (user, provider, providerUserId, claims); } private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) { var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); return user; } // if the external login is OIDC-based, there are certain things we need to preserve to make logout work // this will be different for WS-Fed, SAML2p or other protocols private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) { // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout var idToken = externalResult.Properties.GetTokenValue("id_token"); if (idToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/ExternalProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ExternalProvider { public string DisplayName { get; set; } public string AuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/LoggedOutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoggedOutViewModel { public string PostLogoutRedirectUri { get; set; } public string ClientName { get; set; } public string SignOutIframeUrl { get; set; } public bool AutomaticRedirectAfterSignOut { get; set; } public string LogoutId { get; set; } public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; public string ExternalAuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/LoginInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginInputModel { [Required] public string Username { get; set; } [Required] public string Password { get; set; } public bool RememberLogin { get; set; } public string ReturnUrl { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginViewModel : LoginInputModel { public bool AllowRememberLogin { get; set; } = true; public bool EnableLocalLogin { get; set; } = true; public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/LogoutInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutInputModel { public string LogoutId { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/LogoutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutViewModel : LogoutInputModel { public bool ShowLogoutPrompt { get; set; } = true; } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Account/RedirectViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class RedirectViewModel { public string RedirectUrl { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Consent/ConsentController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This controller processes the consent UI /// [SecurityHeaders] [Authorize] public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IEventService _events; private readonly ILogger _logger; public ConsentController( IIdentityServerInteractionService interaction, IEventService events, ILogger logger) { _interaction = interaction; _events = events; _logger = logger; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { var result = await ProcessConsent(model); if (result.IsRedirect) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context?.IsNativeClient() == true) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", result.RedirectUri); } return result.RedirectUri.IsAllowedRedirect() ? Redirect(result.RedirectUri.SanitizeForRedirect()) : Forbid(); } if (result.HasValidationError) { ModelState.AddModelError(string.Empty, result.ValidationError); } if (result.ShowView) { return View("Index", result.ViewModel); } return View("Error"); } /*****************************************/ /* helper APIs for the ConsentController */ /*****************************************/ private async Task ProcessConsent(ConsentInputModel model) { var result = new ProcessConsentResult(); // validate return url is still valid var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model?.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model?.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); } return result; } private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { return CreateConsentViewModel(model, returnUrl, request); } else { _logger.LogError("No consent request matching request: {0}", returnUrl.SanitizeForLog()); } return null; } private ConsentViewModel CreateConsentViewModel( ConsentInputModel model, string returnUrl, AuthorizationRequest request) { var vm = new ConsentViewModel { RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), Description = model?.Description, ReturnUrl = returnUrl, ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach(var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { var displayName = apiScope.DisplayName ?? apiScope.Name; if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) { displayName += ":" + parsedScopeValue.ParsedParameter; } return new ScopeViewModel { Value = parsedScopeValue.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Consent/ConsentInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentInputModel { public string Button { get; set; } public IEnumerable ScopesConsented { get; set; } public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Consent/ConsentOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentOptions { public static bool EnableOfflineAccess = true; public static string OfflineAccessDisplayName = "Offline Access"; public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Consent/ConsentViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentViewModel : ConsentInputModel { public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public bool AllowRememberConsent { get; set; } public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Consent/ProcessConsentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ProcessConsentResult { public bool IsRedirect => RedirectUri != null; public string RedirectUri { get; set; } public Client Client { get; set; } public bool ShowView => ViewModel != null; public ConsentViewModel ViewModel { get; set; } public bool HasValidationError => ValidationError != null; public string ValidationError { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Consent/ScopeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ScopeViewModel { public string Value { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Emphasize { get; set; } public bool Required { get; set; } public bool Checked { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Device/DeviceAuthorizationInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Device/DeviceAuthorizationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationViewModel : ConsentViewModel { public string UserCode { get; set; } public bool ConfirmUserCode { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Device/DeviceController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [Authorize] [SecurityHeaders] public class DeviceController : Controller { private readonly IDeviceFlowInteractionService _interaction; private readonly IEventService _events; private readonly IOptions _options; private readonly ILogger _logger; public DeviceController( IDeviceFlowInteractionService interaction, IEventService eventService, IOptions options, ILogger logger) { _interaction = interaction; _events = eventService; _options = options; _logger = logger; } [HttpGet] public async Task Index() { string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; string userCode = Request.Query[userCodeParamName]; if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); vm.ConfirmUserCode = true; return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task UserCodeCapture(string userCode) { var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task Callback(DeviceAuthorizationInputModel model) { if (model == null) throw new ArgumentNullException(nameof(model)); var result = await ProcessConsent(model); if (result.HasValidationError) return View("Error"); return View("Success"); } private async Task ProcessConsent(DeviceAuthorizationInputModel model) { var result = new ProcessConsentResult(); var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.UserCode, model); } return result; } private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(userCode); if (request != null) { return CreateConsentViewModel(userCode, model, request); } return null; } private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { UserCode = userCode, Description = model?.Description, RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? DisplayName = apiScope.DisplayName ?? apiScope.Name, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Diagnostics/DiagnosticsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [Authorize] public class DiagnosticsController : Controller { public async Task Index() { var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) { return NotFound(); } var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); return View(model); } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Diagnostics/DiagnosticsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DiagnosticsViewModel { public DiagnosticsViewModel(AuthenticateResult result) { AuthenticateResult = result; if (result.Properties.Items.ContainsKey("client_list")) { var encoded = result.Properties.Items["client_list"]; var bytes = Base64Url.Decode(encoded); var value = Encoding.UTF8.GetString(bytes); Clients = JsonSerializer.Deserialize(value); } } public AuthenticateResult AuthenticateResult { get; } public IEnumerable Clients { get; } = new List(); } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public static class Extensions { /// /// Checks if the redirect URI is for a native client. /// /// public static bool IsNativeClient(this AuthorizationRequest context) { return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); } public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) { controller.HttpContext.Response.StatusCode = 200; controller.HttpContext.Response.Headers["Location"] = ""; return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Grants/GrantsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller allows a user to revoke grants given to clients /// [SecurityHeaders] [Authorize] public class GrantsController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; private readonly IResourceStore _resources; private readonly IEventService _events; public GrantsController(IIdentityServerInteractionService interaction, IClientStore clients, IResourceStore resources, IEventService events) { _interaction = interaction; _clients = clients; _resources = resources; _events = events; } /// /// Show list of grants /// [HttpGet] public async Task Index() { return View("Index", await BuildViewModelAsync()); } /// /// Handle postback to revoke a client /// [HttpPost] [ValidateAntiForgeryToken] public async Task Revoke(string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return RedirectToAction("Index"); } private async Task BuildViewModelAsync() { var grants = await _interaction.GetAllUserGrantsAsync(); var list = new List(); foreach(var grant in grants) { var client = await _clients.FindClientByIdAsync(grant.ClientId); if (client != null) { var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); var item = new GrantViewModel() { ClientId = client.ClientId, ClientName = client.ClientName ?? client.ClientId, ClientLogoUrl = client.LogoUri, ClientUrl = client.ClientUri, Description = grant.Description, Created = grant.CreationTime, Expires = grant.Expiration, IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() }; list.Add(item); } } return new GrantsViewModel { Grants = list }; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Grants/GrantsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class GrantsViewModel { public IEnumerable Grants { get; set; } } public class GrantViewModel { public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public string Description { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Home/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ErrorViewModel { public ErrorViewModel() { } public ErrorViewModel(string error) { Error = new ErrorMessage { Error = error }; } public ErrorMessage Error { get; set; } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/Home/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) { _interaction = interaction; _environment = environment; _logger = logger; } public IActionResult Index() { if (_environment.IsDevelopment()) { // only show in development return View(); } _logger.LogInformation("Homepage is disabled in production. Returning 404."); return NotFound(); } /// /// Shows the error page /// public async Task Error(string errorId) { var vm = new ErrorViewModel(); // retrieve error details from identityserver var message = await _interaction.GetErrorContextAsync(errorId); if (message != null) { vm.Error = message; if (!_environment.IsDevelopment()) { // only show in development message.ErrorDescription = null; } } return View("Error", vm); } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/SecurityHeadersAttribute.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class SecurityHeadersAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { var result = context.Result; if (result is ViewResult) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) { context.HttpContext.Response.Headers.Append("X-Content-Type-Options", "nosniff"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) { context.HttpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; // also consider adding upgrade-insecure-requests once you have HTTPS in place for production //csp += "upgrade-insecure-requests;"; // also an example if you need client images to be displayed from twitter // csp += "img-src 'self' https://pbs.twimg.com;"; // once for standards compliant browsers if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("Content-Security-Policy", csp); } // and once again for IE if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("X-Content-Security-Policy", csp); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy var referrer_policy = "no-referrer"; if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) { context.HttpContext.Response.Headers.Append("Referrer-Policy", referrer_policy); } } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Quickstart/TestUsers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class TestUsers { static readonly object UserAddress = new { street_address = "One Hacker Way", locality = "Heidelberg", postal_code = 69118, country = "Germany" }; public static List Users => new List { new() { SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new (Name, "Alice Smith"), new (GivenName, "Alice"), new (FamilyName, "Smith"), new (Email, "AliceSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://alice.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } }, new() { SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new (Name, "Bob Smith"), new (GivenName, "Bob"), new (FamilyName, "Smith"), new (Email, "BobSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://bob.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } } }; } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/SeedData.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerAspNetIdentity { public class SeedData { public static void EnsureSeedData(string connectionString) { var services = new ServiceCollection(); services.AddLogging(); services.AddDbContext(options => options.UseSqlite(connectionString)); services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); using (var serviceProvider = services.BuildServiceProvider()) { using (var scope = serviceProvider.GetRequiredService().CreateScope()) { var context = scope.ServiceProvider.GetService(); context.Database.Migrate(); var userMgr = scope.ServiceProvider.GetRequiredService>(); var alice = userMgr.FindByNameAsync("alice").Result; if (alice == null) { alice = new ApplicationUser { UserName = "alice", Email = "AliceSmith@email.com", EmailConfirmed = true, }; var result = userMgr.CreateAsync(alice, "Pass123$").Result; if (!result.Succeeded) { throw new Exception(result.Errors.First().Description); } result = userMgr.AddClaimsAsync(alice, new Claim[]{ new Claim(JwtClaimTypes.Name, "Alice Smith"), new Claim(JwtClaimTypes.GivenName, "Alice"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.WebSite, "http://alice.com"), }).Result; if (!result.Succeeded) { throw new Exception(result.Errors.First().Description); } Log.Debug("alice created"); } else { Log.Debug("alice already exists"); } var bob = userMgr.FindByNameAsync("bob").Result; if (bob == null) { bob = new ApplicationUser { UserName = "bob", Email = "BobSmith@email.com", EmailConfirmed = true }; var result = userMgr.CreateAsync(bob, "Pass123$").Result; if (!result.Succeeded) { throw new Exception(result.Errors.First().Description); } result = userMgr.AddClaimsAsync(bob, new Claim[]{ new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim("location", "somewhere") }).Result; if (!result.Succeeded) { throw new Exception(result.Errors.First().Description); } Log.Debug("bob created"); } else { Log.Debug("bob already exists"); } } } } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Account/AccessDenied.cshtml ================================================ 

    Access Denied

    You do not have access to that resource.

    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Account/LoggedOut.cshtml ================================================ @model LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user ViewData["signed-out"] = true; }

    Logout You are now logged out

    @if (Model.PostLogoutRedirectUri != null) {
    Click here to return to the @Model.ClientName application.
    } @if (Model.SignOutIframeUrl != null) { }
    @section scripts { @if (Model.AutomaticRedirectAfterSignOut) { } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Account/Login.cshtml ================================================ @model LoginViewModel ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Account/Logout.cshtml ================================================ @model LogoutViewModel

    Logout

    Would you like to logout of IdentityServer?

    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Device/Success.cshtml ================================================

    Success

    You have successfully authorized the device

    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Device/UserCodeCapture.cshtml ================================================ @model string

    User Code

    Please enter the code displayed on your device.

    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
    @if (Model.ClientLogoUrl != null) { }

    @Model.ClientName is requesting your permission

    @if (Model.ConfirmUserCode) {

    Please confirm that the authorization request quotes the code: @Model.UserCode.

    }

    Uncheck the permissions you do not wish to grant.

    @if (Model.IdentityScopes.Any()) {
    Personal Information
      @foreach (var scope in Model.IdentityScopes) { }
    } @if (Model.ApiScopes.Any()) {
    Application Access
      @foreach (var scope in Model.ApiScopes) { }
    }
    Description
    @if (Model.AllowRememberConsent) {
    }
    @if (Model.ClientUrl != null) { @Model.ClientName }
    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

    Authentication Cookie

    Claims

    @foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in Model.AuthenticateResult.Properties.Items) {
    @prop.Key
    @prop.Value
    } @if (Model.Clients.Any()) {
    Clients
    @{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
    }
    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

    Client Application Permissions

    Below is the list of applications you have given permission to and the resources they have access to.

    @if (Model.Grants.Any() == false) {
    You have not given access to any applications
    } else { foreach (var grant in Model.Grants) {
    @if (grant.ClientLogoUrl != null) { } @grant.ClientName
      @if (grant.Description != null) {
    • @grant.Description
    • }
    • @grant.Created.ToString("yyyy-MM-dd")
    • @if (grant.Expires.HasValue) {
    • @grant.Expires.Value.ToString("yyyy-MM-dd")
    • } @if (grant.IdentityGrantNames.Any()) {
      • @foreach (var name in grant.IdentityGrantNames) {
      • @name
      • }
    • } @if (grant.ApiGrantNames.Any()) {
      • @foreach (var name in grant.ApiGrantNames) {
      • @name
      • }
    • }
    } }
    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @{ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); }

    Welcome to IdentityServer8 (version @version)

    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

    Error

    Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
    @errorDescription
    } }
    @if (request_id != null) {
    Request Id: @request_id
    }
    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

    You are now being returned to the application

    Once complete, you may close this tab.

    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
    @RenderBody()
    @RenderSection("scripts", required: false) ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/appsettings.json ================================================ { "ConnectionStrings": { "DefaultConnection": "Data Source=AspIdUsers.db;" } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/updateUI.ps1 ================================================ iex ((New-Object System.Net.WebClient).DownloadString('https://raw.githubusercontent.com/IdentityServer/IdentityServer8.Quickstart.UI.AspNetIdentity/main/getmain.ps1')) # SIG # Begin signature block # MIIfnQYJKoZIhvcNAQcCoIIfjjCCH4oCAQExDzANBglghkgBZQMEAgEFADB5Bgor # BgEEAYI3AgEEoGswaTA0BgorBgEEAYI3AgEeMCYCAwEAAAQQH8w7YFlLCE63JNLG # KX7zUQIBAAIBAAIBAAIBAAIBADAxMA0GCWCGSAFlAwQCAQUABCAmjChzA8Tk4hB4 # nAolI/h9hR7E40n+o1koeC4dCBgwCqCCDgcwggPFMIICraADAgECAhACrFwmagtA # m48LefKuRiV3MA0GCSqGSIb3DQEBBQUAMGwxCzAJBgNVBAYTAlVTMRUwEwYDVQQK # EwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20xKzApBgNV # BAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0EwHhcNMDYxMTEw # MDAwMDAwWhcNMzExMTEwMDAwMDAwWjBsMQswCQYDVQQGEwJVUzEVMBMGA1UEChMM # RGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSswKQYDVQQD # EyJEaWdpQ2VydCBIaWdoIEFzc3VyYW5jZSBFViBSb290IENBMIIBIjANBgkqhkiG # 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxszlc+b71LvlLS0ypt/lgT/JzSVJtnEqw9WU # NGeiChywX2mmQLHEt7KP0JikqUFZOtPclNY823Q4pErMTSWC90qlUxI47vNJbXGR # fmO2q6Zfw6SE+E9iUb74xezbOJLjBuUIkQzEKEFV+8taiRV+ceg1v01yCT2+OjhQ # W3cxG42zxyRFmqesbQAUWgS3uhPrUQqYQUEiTmVhh4FBUKZ5XIneGUpX1S7mXRxT # LH6YzRoGFqRoc9A0BBNcoXHTWnxV215k4TeHMFYE5RG0KYAS8Xk5iKICEXwnZreI # t3jyygqoOKsKZMK/Zl2VhMGhJR6HXRpQCyASzEG7bgtROLhLywIDAQABo2MwYTAO # BgNVHQ8BAf8EBAMCAYYwDwYDVR0TAQH/BAUwAwEB/zAdBgNVHQ4EFgQUsT7DaQP4 # v0cB1JgmGggC72NkK8MwHwYDVR0jBBgwFoAUsT7DaQP4v0cB1JgmGggC72NkK8Mw # DQYJKoZIhvcNAQEFBQADggEBABwaBpfc15yfPIhmBghXIdshR/gqZ6q/GDJ2QBBX # wYrzetkRZY41+p78RbWe2UwxS7iR6EMsjrN4ztvjU3lx1uUhlAHaVYeaJGT2imbM # 3pw3zag0sWmbI8ieeCIrcEPjVUcxYRnvWMWFL04w9qAxFiPI5+JlFjPLvxoboD34 # yl6LMYtgCIktDAZcUrfE+QqY0RVfnxK+fDZjOL1EpH/kJisKxJdpDemM4sAQV7jI # dhKRVfJIadi8KgJbD0TUIDHb9LpwJl2QYJ68SxcJL7TLHkNoyQcnwdJc9+ohuWgS # nDycv578gFybY83sR6olJ2egN/MAgn1U16n46S4To3foH0owggSRMIIDeaADAgEC # AhAHsEGNpR4UjDMbvN63E4MjMA0GCSqGSIb3DQEBCwUAMGwxCzAJBgNVBAYTAlVT # MRUwEwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5j # b20xKzApBgNVBAMTIkRpZ2lDZXJ0IEhpZ2ggQXNzdXJhbmNlIEVWIFJvb3QgQ0Ew # HhcNMTgwNDI3MTI0MTU5WhcNMjgwNDI3MTI0MTU5WjBaMQswCQYDVQQGEwJVUzEY # MBYGA1UEChMPLk5FVCBGb3VuZGF0aW9uMTEwLwYDVQQDEyguTkVUIEZvdW5kYXRp # b24gUHJvamVjdHMgQ29kZSBTaWduaW5nIENBMIIBIjANBgkqhkiG9w0BAQEFAAOC # AQ8AMIIBCgKCAQEAwQqv4aI0CI20XeYqTTZmyoxsSQgcCBGQnXnufbuDLhAB6GoT # NB7HuEhNSS8ftV+6yq8GztBzYAJ0lALdBjWypMfL451/84AO5ZiZB3V7MB2uxgWo # cV1ekDduU9bm1Q48jmR4SVkLItC+oQO/FIA2SBudVZUvYKeCJS5Ri9ibV7La4oo7 # BJChFiP8uR+v3OU33dgm5BBhWmth4oTyq22zCfP3NO6gBWEIPFR5S+KcefUTYmn2 # o7IvhvxzJsMCrNH1bxhwOyMl+DQcdWiVPuJBKDOO/hAKIxBG4i6ryQYBaKdhDgaA # NSCik0UgZasz8Qgl8n0A73+dISPumD8L/4mdywIDAQABo4IBPzCCATswHQYDVR0O # BBYEFMtck66Im/5Db1ZQUgJtePys4bFaMB8GA1UdIwQYMBaAFLE+w2kD+L9HAdSY # JhoIAu9jZCvDMA4GA1UdDwEB/wQEAwIBhjATBgNVHSUEDDAKBggrBgEFBQcDAzAS # BgNVHRMBAf8ECDAGAQH/AgEAMDQGCCsGAQUFBwEBBCgwJjAkBggrBgEFBQcwAYYY # aHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEsGA1UdHwREMEIwQKA+oDyGOmh0dHA6 # Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9EaWdpQ2VydEhpZ2hBc3N1cmFuY2VFVlJvb3RD # QS5jcmwwPQYDVR0gBDYwNDAyBgRVHSAAMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8v # d3d3LmRpZ2ljZXJ0LmNvbS9DUFMwDQYJKoZIhvcNAQELBQADggEBALNGxKTz6gq6 # clMF01GjC3RmJ/ZAoK1V7rwkqOkY3JDl++v1F4KrFWEzS8MbZsI/p4W31Eketazo # Nxy23RT0zDsvJrwEC3R+/MRdkB7aTecsYmMeMHgtUrl3xEO3FubnQ0kKEU/HBCTd # hR14GsQEccQQE6grFVlglrew+FzehWUu3SUQEp9t+iWpX/KfviDWx0H1azilMX15 # lzJUxK7kCzmflrk5jCOCjKqhOdGJoQqstmwP+07qXO18bcCzEC908P+TYkh0z9gV # rlj7tyW9K9zPVPJZsLRaBp/QjMcH65o9Y1hD1uWtFQYmbEYkT1K9tuXHtQYx1Rpf # /dC8Nbl4iukwggWlMIIEjaADAgECAhAL5Ofkz0TFYBmonpg7pehYMA0GCSqGSIb3 # DQEBCwUAMFoxCzAJBgNVBAYTAlVTMRgwFgYDVQQKEw8uTkVUIEZvdW5kYXRpb24x # MTAvBgNVBAMTKC5ORVQgRm91bmRhdGlvbiBQcm9qZWN0cyBDb2RlIFNpZ25pbmcg # Q0EwHhcNMTgwNjExMDAwMDAwWhcNMjEwNjE1MTIwMDAwWjCBoDEUMBIGA1UEBRML # NjAzIDM4OSAwNjgxCzAJBgNVBAYTAlVTMRMwEQYDVQQIEwpXYXNoaW5ndG9uMRAw # DgYDVQQHEwdSZWRtb25kMSkwJwYDVQQKEyBJZGVudGl0eVNlcnZlciAoLk5FVCBG # b3VuZGF0aW9uKTEpMCcGA1UEAxMgSWRlbnRpdHlTZXJ2ZXIgKC5ORVQgRm91bmRh # dGlvbikwggEiMA0GCSqGSIb3DQEBAQUAA4IBDwAwggEKAoIBAQCwEhFpSIYi3hrr # v/X9BtZkzufk7puhmTCVmQAPNm2R1eZZyMPhfxm5Sh/w/42CzlCG9LFgooha69z3 # uoMMOKJEEQKZ6ByIV+r81o4lrHtSFbe4VlXavjQVFaVVjPSG6vWGykfHVCAeVpjx # fVk/HH6tEX506lBiHgOrQGogoQrwdVnObc3c6RiVSIuvFeCoHvk2GgiqyzFER7iO # R1055npVSAAAdxBvPA6KREcLb/qHukYCJZX4mY/SajBXwxupSnhRDbYhb+qHpFIL # x7s/azxg7tVRpVh49oJimHA2uZ/jzh/KgUsUe9MFzT7KPduBK/pfX/fXED9Pt1NN # 48VfPSuzAgMBAAGjggIeMIICGjAfBgNVHSMEGDAWgBTLXJOuiJv+Q29WUFICbXj8 # rOGxWjAdBgNVHQ4EFgQU3CQnBPLvFkovKU/is0/LgQF/49swNAYDVR0RBC0wK6Ap # BggrBgEFBQcIA6AdMBsMGVVTLVdBU0hJTkdUT04tNjAzIDM4OSAwNjgwDgYDVR0P # AQH/BAQDAgeAMBMGA1UdJQQMMAoGCCsGAQUFBwMDMIGZBgNVHR8EgZEwgY4wRaBD # oEGGP2h0dHA6Ly9jcmwzLmRpZ2ljZXJ0LmNvbS9ORVRGb3VuZGF0aW9uUHJvamVj # dHNDb2RlU2lnbmluZ0NBLmNybDBFoEOgQYY/aHR0cDovL2NybDQuZGlnaWNlcnQu # Y29tL05FVEZvdW5kYXRpb25Qcm9qZWN0c0NvZGVTaWduaW5nQ0EuY3JsMEwGA1Ud # IARFMEMwNwYJYIZIAYb9bAMBMCowKAYIKwYBBQUHAgEWHGh0dHBzOi8vd3d3LmRp # Z2ljZXJ0LmNvbS9DUFMwCAYGZ4EMAQQBMIGEBggrBgEFBQcBAQR4MHYwJAYIKwYB # BQUHMAGGGGh0dHA6Ly9vY3NwLmRpZ2ljZXJ0LmNvbTBOBggrBgEFBQcwAoZCaHR0 # cDovL2NhY2VydHMuZGlnaWNlcnQuY29tL05FVEZvdW5kYXRpb25Qcm9qZWN0c0Nv # ZGVTaWduaW5nQ0EuY3J0MAwGA1UdEwEB/wQCMAAwDQYJKoZIhvcNAQELBQADggEB # AH0FqXX4p5RlC27jX6tcTEf2HT4mAiqosnYU/napWgQE2U9m73IZO+2MuiQWkUQi # 2PLjbQQcOwfMwkt0SDaSAlfC1zhjZkZb2NcpJRg0cUAjcDzqh6hTzXRVJPD/UrW2 # a5qBhYnSQDSWbYnVwfAQFFvnQcR5i/xnoOxq7+3LIvHoJafpsxcAFS57Vdsuw91u # keB6uasOfvdd06Mpl9BLWZHpyEdnPIKMv6ALibTdw9lNzCQ+EmdT5Fwky8wHE8BH # hhAdjSuGiyd+AzR3IuL96Q41h34c7pL827atOHwkkjUx+QTVkXbYoal6wwBKhi6I # QJEhT0s/yyFFM7BrLhQpRSsxghDsMIIQ6AIBATBuMFoxCzAJBgNVBAYTAlVTMRgw # FgYDVQQKEw8uTkVUIEZvdW5kYXRpb24xMTAvBgNVBAMTKC5ORVQgRm91bmRhdGlv # biBQcm9qZWN0cyBDb2RlIFNpZ25pbmcgQ0ECEAvk5+TPRMVgGaiemDul6FgwDQYJ # YIZIAWUDBAIBBQCggYQwGAYKKwYBBAGCNwIBDDEKMAigAoAAoQKAADAZBgkqhkiG # 9w0BCQMxDAYKKwYBBAGCNwIBBDAcBgorBgEEAYI3AgELMQ4wDAYKKwYBBAGCNwIB # FTAvBgkqhkiG9w0BCQQxIgQg3M8y2Uovs3GZZGILEzOaZ5eGje/9PkpzoByKnbYv # zTYwDQYJKoZIhvcNAQEBBQAEggEAnU9xhSBP/Rv/Cgvmzp0LcG8pjxPlbjvPef7V # xvkeQzTvMHYg0LiveMH1y94PyfBdxqUAGRuCblBOmlECw24w8Hdv8dY2LQMhqJu8 # Qt08UVYtiJQBHwclFmnK8ER8g/Wc5LPsuvzoYzTvvr/FS2wSCaMV7p2WHIwjJxXR # sLaccoU2fuPbmE1WVPsjy3ttzkmfyhbl3Mc2QXiLNTQJ+gSNBg2s2eSzf7eDcM2Z # kysuAEdu4kKhSsHOhkkpBHffplg9pYrehbUq8QJ+T/9e0vHPbbP5xIPaRqvBEMWy # ydh0LWvK6GnJ+/yPHjddtrjpY4ncTlK5ii+wyzyZc9k4rryOKaGCDsgwgg7EBgor # BgEEAYI3AwMBMYIOtDCCDrAGCSqGSIb3DQEHAqCCDqEwgg6dAgEDMQ8wDQYJYIZI # AWUDBAIBBQAwdwYLKoZIhvcNAQkQAQSgaARmMGQCAQEGCWCGSAGG/WwHATAxMA0G # CWCGSAFlAwQCAQUABCDIQfwOJSjewgHOlovqbOQetKuLbiiSjibgzLTQTnzqLAIQ # BSEyueNQrxAikpAH0FblzhgPMjAyMDA2MjUxNDI4NTNaoIILuzCCBoIwggVqoAMC # AQICEATNP4VornbGG7D+cWDMp20wDQYJKoZIhvcNAQELBQAwcjELMAkGA1UEBhMC # VVMxFTATBgNVBAoTDERpZ2lDZXJ0IEluYzEZMBcGA1UECxMQd3d3LmRpZ2ljZXJ0 # LmNvbTExMC8GA1UEAxMoRGlnaUNlcnQgU0hBMiBBc3N1cmVkIElEIFRpbWVzdGFt # cGluZyBDQTAeFw0xOTEwMDEwMDAwMDBaFw0zMDEwMTcwMDAwMDBaMEwxCzAJBgNV # BAYTAlVTMRcwFQYDVQQKEw5EaWdpQ2VydCwgSW5jLjEkMCIGA1UEAxMbVElNRVNU # QU1QLVNIQTI1Ni0yMDE5LTEwLTE1MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIB # CgKCAQEA6WQ1nPqpmGVkG+QX3LgpNsxnCViFTTDgyf/lOzwRKFCvBzHiXQkYwvaJ # jGkIBCPgdy2dFeW46KFqjv/UrtJ6Fu/4QbUdOXXBzy+nrEV+lG2sAwGZPGI+fnr9 # RZcxtPq32UI+p1Wb31pPWAKoMmkiE76Lgi3GmKtrm7TJ8mURDHQNsvAIlnTE6LJI # oqEUpfj64YlwRDuN7/uk9MO5vRQs6wwoJyWAqxBLFhJgC2kijE7NxtWyZVkh4Hws # Eo1wDo+KyuDT17M5d1DQQiwues6cZ3o4d1RA/0+VBCDU68jOhxQI/h2A3dDnK3jq # vx9wxu5CFlM2RZtTGUlinXoCm5UUowIDAQABo4IDODCCAzQwDgYDVR0PAQH/BAQD # AgeAMAwGA1UdEwEB/wQCMAAwFgYDVR0lAQH/BAwwCgYIKwYBBQUHAwgwggG/BgNV # HSAEggG2MIIBsjCCAaEGCWCGSAGG/WwHATCCAZIwKAYIKwYBBQUHAgEWHGh0dHBz # Oi8vd3d3LmRpZ2ljZXJ0LmNvbS9DUFMwggFkBggrBgEFBQcCAjCCAVYeggFSAEEA # bgB5ACAAdQBzAGUAIABvAGYAIAB0AGgAaQBzACAAQwBlAHIAdABpAGYAaQBjAGEA # dABlACAAYwBvAG4AcwB0AGkAdAB1AHQAZQBzACAAYQBjAGMAZQBwAHQAYQBuAGMA # ZQAgAG8AZgAgAHQAaABlACAARABpAGcAaQBDAGUAcgB0ACAAQwBQAC8AQwBQAFMA # IABhAG4AZAAgAHQAaABlACAAUgBlAGwAeQBpAG4AZwAgAFAAYQByAHQAeQAgAEEA # ZwByAGUAZQBtAGUAbgB0ACAAdwBoAGkAYwBoACAAbABpAG0AaQB0ACAAbABpAGEA # YgBpAGwAaQB0AHkAIABhAG4AZAAgAGEAcgBlACAAaQBuAGMAbwByAHAAbwByAGEA # dABlAGQAIABoAGUAcgBlAGkAbgAgAGIAeQAgAHIAZQBmAGUAcgBlAG4AYwBlAC4w # CwYJYIZIAYb9bAMVMB8GA1UdIwQYMBaAFPS24SAd/imu0uRhpbKiJbLIFzVuMB0G # A1UdDgQWBBRWUw/BxgenTdfYbldygFBM5OyewTBxBgNVHR8EajBoMDKgMKAuhixo # dHRwOi8vY3JsMy5kaWdpY2VydC5jb20vc2hhMi1hc3N1cmVkLXRzLmNybDAyoDCg # LoYsaHR0cDovL2NybDQuZGlnaWNlcnQuY29tL3NoYTItYXNzdXJlZC10cy5jcmww # gYUGCCsGAQUFBwEBBHkwdzAkBggrBgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNl # cnQuY29tME8GCCsGAQUFBzAChkNodHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20v # RGlnaUNlcnRTSEEyQXNzdXJlZElEVGltZXN0YW1waW5nQ0EuY3J0MA0GCSqGSIb3 # DQEBCwUAA4IBAQAug6FEBUoE47kyUvrZgfAau/gJjSO5PdiSoeZGHEovbno8Y243 # F6Mav1gjskOclINOOQmwLOjH4eLM7ct5a87eIwFH7ZVUgeCAexKxrwKGqTpzav74 # n8GN0SGM5CmCw4oLYAACnR9HxJ+0CmhTf1oQpvgi5vhTkjFf2IKDLW0TQq6DwRBO # pCT0R5zeDyJyd1x/T+k5mCtXkkTX726T2UPHBDNjUTdWnkcEEcOjWFQh2OKOVtdJ # P1f8Cp8jXnv0lI3dnRq733oqptJFplUMj/ZMivKWz4lG3DGykZCjXzMwYFX1/Gsw # rKHt5EdOM55naii1TcLtW5eC+MupCGxTCbT3MIIFMTCCBBmgAwIBAgIQCqEl1tYy # G35B5AXaNpfCFTANBgkqhkiG9w0BAQsFADBlMQswCQYDVQQGEwJVUzEVMBMGA1UE # ChMMRGlnaUNlcnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMSQwIgYD # VQQDExtEaWdpQ2VydCBBc3N1cmVkIElEIFJvb3QgQ0EwHhcNMTYwMTA3MTIwMDAw # WhcNMzEwMTA3MTIwMDAwWjByMQswCQYDVQQGEwJVUzEVMBMGA1UEChMMRGlnaUNl # cnQgSW5jMRkwFwYDVQQLExB3d3cuZGlnaWNlcnQuY29tMTEwLwYDVQQDEyhEaWdp # Q2VydCBTSEEyIEFzc3VyZWQgSUQgVGltZXN0YW1waW5nIENBMIIBIjANBgkqhkiG # 9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvdAy7kvNj3/dqbqCmcU5VChXtiNKxA4HRTNR # EH3Q+X1NaH7ntqD0jbOI5Je/YyGQmL8TvFfTw+F+CNZqFAA49y4eO+7MpvYyWf5f # ZT/gm+vjRkcGGlV+Cyd+wKL1oODeIj8O/36V+/OjuiI+GKwR5PCZA207hXwJ0+5d # yJoLVOOoCXFr4M8iEA91z3FyTgqt30A6XLdR4aF5FMZNJCMwXbzsPGBqrC8HzP3w # 6kfZiFBe/WZuVmEnKYmEUeaC50ZQ/ZQqLKfkdT66mA+Ef58xFNat1fJky3seBdCE # GXIX8RcG7z3N1k3vBkL9olMqT4UdxB08r8/arBD13ays6Vb/kwIDAQABo4IBzjCC # AcowHQYDVR0OBBYEFPS24SAd/imu0uRhpbKiJbLIFzVuMB8GA1UdIwQYMBaAFEXr # oq/0ksuCMS1Ri6enIZ3zbcgPMBIGA1UdEwEB/wQIMAYBAf8CAQAwDgYDVR0PAQH/ # BAQDAgGGMBMGA1UdJQQMMAoGCCsGAQUFBwMIMHkGCCsGAQUFBwEBBG0wazAkBggr # BgEFBQcwAYYYaHR0cDovL29jc3AuZGlnaWNlcnQuY29tMEMGCCsGAQUFBzAChjdo # dHRwOi8vY2FjZXJ0cy5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290 # Q0EuY3J0MIGBBgNVHR8EejB4MDqgOKA2hjRodHRwOi8vY3JsNC5kaWdpY2VydC5j # b20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMDqgOKA2hjRodHRwOi8vY3Js # My5kaWdpY2VydC5jb20vRGlnaUNlcnRBc3N1cmVkSURSb290Q0EuY3JsMFAGA1Ud # IARJMEcwOAYKYIZIAYb9bAACBDAqMCgGCCsGAQUFBwIBFhxodHRwczovL3d3dy5k # aWdpY2VydC5jb20vQ1BTMAsGCWCGSAGG/WwHATANBgkqhkiG9w0BAQsFAAOCAQEA # cZUS6VGHVmnN793afKpjerN4zwY3QITvS4S/ys8DAv3Fp8MOIEIsr3fzKx8MIVoq # twU0HWqumfgnoma/Capg33akOpMP+LLR2HwZYuhegiUexLoceywh4tZbLBQ1QwRo # stt1AuByx5jWPGTlH0gQGF+JOGFNYkYkh2OMkVIsrymJ5Xgf1gsUpYDXEkdws3XV # k4WTfraSZ/tTYYmo9WuWwPRYaQ18yAGxuSh1t5ljhSKMYcp5lH5Z/IwP42+1ASa2 # bKXuh1Eh5Fhgm7oMLSttosR+u8QlK0cCCHxJrhO24XxCQijGGFbPQTS2Zl22dHv1 # VjMiLyI2skuiSpXY9aaOUjGCAk0wggJJAgEBMIGGMHIxCzAJBgNVBAYTAlVTMRUw # EwYDVQQKEwxEaWdpQ2VydCBJbmMxGTAXBgNVBAsTEHd3dy5kaWdpY2VydC5jb20x # MTAvBgNVBAMTKERpZ2lDZXJ0IFNIQTIgQXNzdXJlZCBJRCBUaW1lc3RhbXBpbmcg # Q0ECEATNP4VornbGG7D+cWDMp20wDQYJYIZIAWUDBAIBBQCggZgwGgYJKoZIhvcN # AQkDMQ0GCyqGSIb3DQEJEAEEMBwGCSqGSIb3DQEJBTEPFw0yMDA2MjUxNDI4NTNa # MCsGCyqGSIb3DQEJEAIMMRwwGjAYMBYEFAMlvVBe2pYwLcIvT6AeTCi+KDTFMC8G # CSqGSIb3DQEJBDEiBCAvoc+rYs7skpQOtoc7TrhDopI+gY0L5n1xBAzyBSyC8TAN # BgkqhkiG9w0BAQEFAASCAQCRIBcC0oeOOjuPQ2x9QeqG+lmZXas8Y2P9oaXzmbFS # 6okl0cflVVL3m9EvPJ3ofob8aQ9PRmyJRIaDP24R8GGS9m8ZTLZJvg8wPOtoE/3t # mLfPaY1KFSqtKWF8sLSHkWJzF50FvboB00E4f1mRDBCAZPNfyKSBYiW7b60Blcnz # mf721nSHxPPbGtoU4ObXcR4qHyWbrlIMcihIkkwf8HEU3MqqsLSnpGoKeNvS/quz # N2mp2eJl9YezOn+yzSkDKSQKg5x+2ZI/2UCstJaq73ahuRSpcdw3Lu6FgWs7n8Wc # N6gU/Mpvb3GqgwZWpbSoa9PusWSNLjOyrWAwjhdeBCvV # SIG # End signature block ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/wwwroot/js/signin-redirect.js ================================================ // window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/IdentityServerAspNetIdentity/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Controllers/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class HomeController : Controller { private readonly ILogger _logger; public HomeController(ILogger logger) { _logger = logger; } public IActionResult Index() { return View(); } public async Task CallApi() { var accessToken = await HttpContext.GetTokenAsync("access_token"); var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var content = await client.GetStringAsync("https://localhost:6001/identity"); var obj = JsonSerializer.Deserialize(content); ViewBag.Json = obj.ToString(); return View("json"); } public IActionResult Logout() { return SignOut("Cookies", "oidc"); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Mvc; global using System.Diagnostics; global using System.IdentityModel.Tokens.Jwt; global using System.Net.Http.Headers; global using System.Text.Json; ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Models/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class ErrorViewModel { public string RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/MvcClient.csproj ================================================ true PreserveNewest ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ JwtSecurityTokenHandler.DefaultMapInboundClaims = false; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); builder.Services .AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://localhost:5001"; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.Scope.Add("api1"); options.SaveTokens = true; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); else app.UseExceptionHandler("/Home/Error"); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapDefaultControllerRoute().RequireAuthorization(); await app.RunAsync(); } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Properties/launchSettings.json ================================================ { "profiles": { "MvcClient": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5002" } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Views/Home/Index.cshtml ================================================ @using Microsoft.AspNetCore.Authentication

    Claims

    @foreach (var claim in User.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items) {
    @prop.Key
    @prop.Value
    }
    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Views/Home/Privacy.cshtml ================================================ @{ ViewData["Title"] = "Privacy Policy"; }

    @ViewData["Title"]

    Use this page to detail your site's privacy policy.

    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ ViewData["Title"] = "Error"; }

    Error.

    An error occurred while processing your request.

    @if (Model.ShowRequestId) {

    Request ID: @Model.RequestId

    }

    Development Mode

    Swapping to Development environment will display more detailed information about the error that occurred.

    The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app.

    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Views/Shared/_Layout.cshtml ================================================ @ViewData["Title"] - MvcClient
    @RenderBody()
    @RenderSection("Scripts", required: false) ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Views/Shared/_ValidationScriptsPartial.cshtml ================================================  ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Views/Shared/json.cshtml ================================================
    @ViewBag.Json
    ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Views/_ViewImports.cshtml ================================================ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/appsettings.json ================================================ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/wwwroot/css/site.css ================================================ /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification for details on configuring this project to bundle and minify static web assets. */ a.navbar-brand { white-space: normal; text-align: center; word-break: break-all; } /* Provide sufficient contrast against white background */ a { color: #0366d6; } .btn-primary { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } /* Sticky footer styles -------------------------------------------------- */ html { font-size: 14px; } @media (min-width: 768px) { html { font-size: 16px; } } .border-top { border-top: 1px solid #e5e5e5; } .border-bottom { border-bottom: 1px solid #e5e5e5; } .box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); } button.accept-policy { font-size: 1rem; line-height: inherit; } /* Sticky footer styles -------------------------------------------------- */ html { position: relative; min-height: 100%; } body { /* Margin bottom by footer height */ margin-bottom: 60px; } .footer { position: absolute; bottom: 0; width: 100%; white-space: nowrap; line-height: 60px; /* Vertically center the text there */ } ================================================ FILE: samples/Quickstarts/6_AspNetIdentity/src/MvcClient/wwwroot/js/site.js ================================================ // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification // for details on configuring this project to bundle and minify static web assets. // Write your JavaScript code. ================================================ FILE: samples/Quickstarts/Directory.Build.props ================================================ ================================================ FILE: samples/Quickstarts/Quickstart.sln.licenseheader ================================================ extensions: designer.cs generated.cs extensions: .cs /* Copyright (c) 2024 HigginsSoft Written by Alexander Higgins https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code for this software can be found at https://github.com/alexhiggins732/IdentityServer8 The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ================================================ FILE: samples/Quickstarts/Shared/src/Api/Api.csproj ================================================ ================================================ FILE: samples/Quickstarts/Shared/src/Api/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.Mvc; Console.Title = "API"; var builder = WebApplication.CreateBuilder(args); var services = builder.Services; // accepts any access token issued by identity server // adds an authorization policy for scope 'api1' services .AddAuthorization(options => { options.AddPolicy("ApiScope", policy => { policy .RequireAuthenticatedUser() .RequireClaim("scope", "api1"); }); }) .AddCors(options => { // this defines a CORS policy called "default" options.AddPolicy("default", policy => { policy.WithOrigins("https://localhost:5003") .AllowAnyHeader() .AllowAnyMethod(); }); }) .AddControllers(); // accepts any access token issued by identity server services .AddAuthentication("Bearer") .AddJwtBearer("Bearer", options => { options.Authority = "https://localhost:5001"; options.TokenValidationParameters = new() { ValidateAudience = false }; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); app .UseRouting() .UseAuthentication() .UseAuthorization() .UseCors("default"); app.MapGet("/identity", (HttpContext context) => new JsonResult(context?.User?.Claims.Select(c => new { c.Type, c.Value })) ).RequireAuthorization("ApiScope"); await app.RunAsync(); } ================================================ FILE: samples/Quickstarts/Shared/src/Api/Properties/launchSettings.json ================================================ { "profiles": { "SelfHost": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:6001" } } } ================================================ FILE: samples/Quickstarts/Shared/src/Client/Client.csproj ================================================ Exe ================================================ FILE: samples/Quickstarts/Shared/src/Client/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityModel.Client; using System.Diagnostics; using System.Text.Json; // discover endpoints from metadata using HttpClient client = new(); var disco = await client.GetDiscoveryDocumentAsync("https://localhost:5001"); if (disco.IsError) Exit(disco.Error); // request token var tokenResponse = await client.RequestClientCredentialsTokenAsync( new() { Address = disco.TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" }); if (tokenResponse.IsError) Exit(tokenResponse.Error); Console.WriteLine(tokenResponse.Json); Console.WriteLine("\n\n"); // call api client.SetBearerToken(tokenResponse.AccessToken); var response = await client.GetAsync("https://localhost:6001/identity"); if (!response.IsSuccessStatusCode) Console.WriteLine(response.StatusCode); else { var responseJson = await response.Content.ReadAsStringAsync(); var obj = JsonSerializer.Deserialize(responseJson); Console.WriteLine(obj); } Exit("Done!", 0); void Exit(string message, int exitCode = 1) { Console.WriteLine(message); if (Debugger.IsAttached) Debugger.Break(); Environment.Exit(exitCode); } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Config.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public static class Config { public static IEnumerable IdentityResources => new List { new IdentityResources.OpenId(), new IdentityResources.Profile(), }; public static IEnumerable ApiScopes => new List { new ApiScope("api1", "My API") }; public static IEnumerable Clients => new List { // machine to machine client new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, // scopes that client has access to AllowedScopes = { "api1" } }, // interactive ASP.NET Core MVC client new Client { ClientId = "mvc", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, // where to redirect to after login RedirectUris = { "https://localhost:5002/signin-oidc" }, // where to redirect to after logout PostLogoutRedirectUris = { "https://localhost:5002/signout-callback-oidc" }, AllowedScopes = new List { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, "api1" } } }; } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using IdentityServerHost.Quickstart.UI; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Hosting; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Options; global using Microsoft.IdentityModel.Tokens; global using Serilog; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System; global using System.ComponentModel.DataAnnotations; global using System.Security.Claims; global using System.Text; global using System.Text.Json; global using static IdentityModel.JwtClaimTypes; global using IdentityServerClaimValueTypes = IdentityServer8.IdentityServerConstants.ClaimValueTypes; global using ILogger = Microsoft.Extensions.Logging.ILogger; ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/IdentityServer.csproj ================================================ ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ConfigureLogger(); try { Log.Information("Starting host..."); var builder = WebApplication.CreateBuilder(args); var services = builder.Services; services.AddControllersWithViews(); services .AddIdentityServer() .AddInMemoryIdentityResources(Config.IdentityResources) .AddInMemoryApiScopes(Config.ApiScopes) .AddInMemoryClients(Config.Clients) .AddTestUsers(TestUsers.Users) .AddDeveloperSigningCredential(); services.AddAuthentication() .AddGoogle("Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ClientId = ""; options.ClientSecret = ""; }) .AddOpenIdConnect("oidc", "Demo IdentityServer", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.SaveTokens = true; options.Authority = "https://demo.identityserver8.io/"; options.ClientId = "interactive.confidential"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.TokenValidationParameters = new() { NameClaimType = "name", RoleClaimType = "role" }; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); app.UseStaticFiles() .UseRouting() .UseIdentityServer() .UseAuthorization(); app.MapDefaultControllerRoute(); await app.RunAsync(); } return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } void ConfigureLogger() => Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() // uncomment to write to Azure diagnostics stream //.WriteTo.File( // @"D:\home\LogFiles\Application\identityserver.txt", // fileSizeLimitBytes: 1_000_000, // rollOnFileSizeLimit: true, // shared: true, // flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) .CreateLogger(); ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Properties/launchSettings.json ================================================ { "profiles": { "SelfHost": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001" } } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/AccountController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval /// [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController( IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; } /// /// Entry point into the login workflow /// [HttpGet] public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); if (vm.IsExternalLoginOnly) { // we only have one option for logging in and it's an external provider return returnUrl.IsAllowedRedirect() ? RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl = returnUrl.SanitizeForRedirect() }) : Forbid(); } return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (ModelState.IsValid) { // validate username/password against in-memory store if (_users.ValidateCredentials(model.Username, model.Password)) { var user = _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.Client.ClientId)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username }; await HttpContext.SignInAsync(isuser, props); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task LoginCancel(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else { // since we don't have a valid context, then we just go back to the home page return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } } /// /// Show logout page /// [HttpGet] public async Task Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); } /// /// Handle logout page postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await HttpContext.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); } [HttpGet] public IActionResult AccessDenied() { return View(); } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) { var local = context.IdP == IdentityServer8.IdentityServerConstants.LocalIdentityProvider; // this is meant to short circuit the UI and only trigger the one external IdP var vm = new LoginViewModel { EnableLocalLogin = local, ReturnUrl = returnUrl, Username = context?.LoginHint, }; if (!local) { vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; } return vm; } var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes .Where(x => x.DisplayName != null) .Select(x => new ExternalProvider { DisplayName = x.DisplayName ?? x.Name, AuthenticationScheme = x.Name }).ToList(); var allowLocal = true; if (context?.Client.ClientId != null) { var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); if (client != null) { allowLocal = client.EnableLocalLogin; if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) { providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); } } } return new LoginViewModel { AllowRememberLogin = AccountOptions.AllowRememberLogin, EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, ReturnUrl = returnUrl, Username = context?.LoginHint, ExternalProviders = providers.ToArray() }; } private async Task BuildLoginViewModelAsync(LoginInputModel model) { var vm = await BuildLoginViewModelAsync(model.ReturnUrl); vm.Username = model.Username; vm.RememberLogin = model.RememberLogin; return vm; } private async Task BuildLogoutViewModelAsync(string logoutId) { var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; if (User?.Identity.IsAuthenticated != true) { // if the user is not authenticated, then just show logged out page vm.ShowLogoutPrompt = false; return vm; } var context = await _interaction.GetLogoutContextAsync(logoutId); if (context?.ShowSignoutPrompt == false) { // it's safe to automatically sign-out vm.ShowLogoutPrompt = false; return vm; } // show the logout prompt. this prevents attacks where the user // is automatically signed out by another malicious web page. return vm; } private async Task BuildLoggedOutViewModelAsync(string logoutId) { // get context information (client name, post logout redirect URI and iframe for federated signout) var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LoggedOutViewModel { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; if (User?.Identity.IsAuthenticated == true) { var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; if (idp != null && idp != IdentityServer8.IdentityServerConstants.LocalIdentityProvider) { var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); if (providerSupportsSignout) { if (vm.LogoutId == null) { // if there's no current logout context, we need to create one // this captures necessary info from the current logged in user // before we signout and redirect away to the external IdP for signout vm.LogoutId = await _interaction.CreateLogoutContextAsync(); } vm.ExternalAuthenticationScheme = idp; } } } return vm; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/AccountOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI { public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/ExternalController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly ILogger _logger; private readonly IEventService _events; public ExternalController( IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService events, ILogger logger, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _logger = logger; _events = events; } /// /// initiate roundtrip to external authentication provider /// [HttpGet] public IActionResult Challenge(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; // validate returnUrl - either it is a valid OIDC URL or back to a local page if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } // start challenge and roundtrip the return URL and scheme var props = new AuthenticationProperties { RedirectUri = Url.Action(nameof(Callback)), Items = { { "returnUrl", returnUrl }, { "scheme", scheme }, } }; return Challenge(props, scheme); } /// /// Post processing of external authentication /// [HttpGet] public async Task Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = AutoProvisionUser(provider, providerUserId, claims); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); return (user, provider, providerUserId, claims); } private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) { var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); return user; } // if the external login is OIDC-based, there are certain things we need to preserve to make logout work // this will be different for WS-Fed, SAML2p or other protocols private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) { // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout var idToken = externalResult.Properties.GetTokenValue("id_token"); if (idToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); } } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/ExternalProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ExternalProvider { public string DisplayName { get; set; } public string AuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/LoggedOutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoggedOutViewModel { public string PostLogoutRedirectUri { get; set; } public string ClientName { get; set; } public string SignOutIframeUrl { get; set; } public bool AutomaticRedirectAfterSignOut { get; set; } public string LogoutId { get; set; } public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; public string ExternalAuthenticationScheme { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/LoginInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginInputModel { [Required] public string Username { get; set; } [Required] public string Password { get; set; } public bool RememberLogin { get; set; } public string ReturnUrl { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginViewModel : LoginInputModel { public bool AllowRememberLogin { get; set; } = true; public bool EnableLocalLogin { get; set; } = true; public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/LogoutInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutInputModel { public string LogoutId { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/LogoutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutViewModel : LogoutInputModel { public bool ShowLogoutPrompt { get; set; } = true; } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Account/RedirectViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class RedirectViewModel { public string RedirectUrl { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Consent/ConsentController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This controller processes the consent UI /// [SecurityHeaders] [Authorize] public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IEventService _events; private readonly ILogger _logger; public ConsentController( IIdentityServerInteractionService interaction, IEventService events, ILogger logger) { _interaction = interaction; _events = events; _logger = logger; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { var result = await ProcessConsent(model); if (result.IsRedirect) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context?.IsNativeClient() == true) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", result.RedirectUri); } return result.RedirectUri.IsAllowedRedirect() ? Redirect(result.RedirectUri.SanitizeForRedirect()) : Forbid(); } if (result.HasValidationError) { ModelState.AddModelError(string.Empty, result.ValidationError); } if (result.ShowView) { return View("Index", result.ViewModel); } return View("Error"); } /*****************************************/ /* helper APIs for the ConsentController */ /*****************************************/ private async Task ProcessConsent(ConsentInputModel model) { var result = new ProcessConsentResult(); // validate return url is still valid var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model?.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model?.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); } return result; } private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { return CreateConsentViewModel(model, returnUrl, request); } else { _logger.LogError("No consent request matching request: {0}", returnUrl.SanitizeForLog()); } return null; } private ConsentViewModel CreateConsentViewModel( ConsentInputModel model, string returnUrl, AuthorizationRequest request) { var vm = new ConsentViewModel { RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), Description = model?.Description, ReturnUrl = returnUrl, ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach(var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { var displayName = apiScope.DisplayName ?? apiScope.Name; if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) { displayName += ":" + parsedScopeValue.ParsedParameter; } return new ScopeViewModel { Value = parsedScopeValue.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Consent/ConsentInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentInputModel { public string Button { get; set; } public IEnumerable ScopesConsented { get; set; } public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Consent/ConsentOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentOptions { public static bool EnableOfflineAccess = true; public static string OfflineAccessDisplayName = "Offline Access"; public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Consent/ConsentViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentViewModel : ConsentInputModel { public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public bool AllowRememberConsent { get; set; } public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Consent/ProcessConsentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ProcessConsentResult { public bool IsRedirect => RedirectUri != null; public string RedirectUri { get; set; } public Client Client { get; set; } public bool ShowView => ViewModel != null; public ConsentViewModel ViewModel { get; set; } public bool HasValidationError => ValidationError != null; public string ValidationError { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Consent/ScopeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ScopeViewModel { public string Value { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Emphasize { get; set; } public bool Required { get; set; } public bool Checked { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Device/DeviceAuthorizationInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Device/DeviceAuthorizationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationViewModel : ConsentViewModel { public string UserCode { get; set; } public bool ConfirmUserCode { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Device/DeviceController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [Authorize] [SecurityHeaders] public class DeviceController : Controller { private readonly IDeviceFlowInteractionService _interaction; private readonly IEventService _events; private readonly IOptions _options; private readonly ILogger _logger; public DeviceController( IDeviceFlowInteractionService interaction, IEventService eventService, IOptions options, ILogger logger) { _interaction = interaction; _events = eventService; _options = options; _logger = logger; } [HttpGet] public async Task Index() { string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; string userCode = Request.Query[userCodeParamName]; if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); vm.ConfirmUserCode = true; return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task UserCodeCapture(string userCode) { var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task Callback(DeviceAuthorizationInputModel model) { if (model == null) throw new ArgumentNullException(nameof(model)); var result = await ProcessConsent(model); if (result.HasValidationError) return View("Error"); return View("Success"); } private async Task ProcessConsent(DeviceAuthorizationInputModel model) { var result = new ProcessConsentResult(); var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.UserCode, model); } return result; } private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(userCode); if (request != null) { return CreateConsentViewModel(userCode, model, request); } return null; } private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { UserCode = userCode, Description = model?.Description, RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? DisplayName = apiScope.DisplayName ?? apiScope.Name, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Diagnostics/DiagnosticsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [Authorize] public class DiagnosticsController : Controller { public async Task Index() { var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) { return NotFound(); } var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); return View(model); } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Diagnostics/DiagnosticsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DiagnosticsViewModel { public DiagnosticsViewModel(AuthenticateResult result) { AuthenticateResult = result; if (result.Properties.Items.ContainsKey("client_list")) { var encoded = result.Properties.Items["client_list"]; var bytes = Base64Url.Decode(encoded); var value = Encoding.UTF8.GetString(bytes); Clients = JsonSerializer.Deserialize(value); } } public AuthenticateResult AuthenticateResult { get; } public IEnumerable Clients { get; } = new List(); } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public static class Extensions { /// /// Checks if the redirect URI is for a native client. /// /// public static bool IsNativeClient(this AuthorizationRequest context) { return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); } public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) { controller.HttpContext.Response.StatusCode = 200; controller.HttpContext.Response.Headers["Location"] = ""; return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Grants/GrantsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller allows a user to revoke grants given to clients /// [SecurityHeaders] [Authorize] public class GrantsController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; private readonly IResourceStore _resources; private readonly IEventService _events; public GrantsController(IIdentityServerInteractionService interaction, IClientStore clients, IResourceStore resources, IEventService events) { _interaction = interaction; _clients = clients; _resources = resources; _events = events; } /// /// Show list of grants /// [HttpGet] public async Task Index() { return View("Index", await BuildViewModelAsync()); } /// /// Handle postback to revoke a client /// [HttpPost] [ValidateAntiForgeryToken] public async Task Revoke(string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return RedirectToAction("Index"); } private async Task BuildViewModelAsync() { var grants = await _interaction.GetAllUserGrantsAsync(); var list = new List(); foreach(var grant in grants) { var client = await _clients.FindClientByIdAsync(grant.ClientId); if (client != null) { var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); var item = new GrantViewModel() { ClientId = client.ClientId, ClientName = client.ClientName ?? client.ClientId, ClientLogoUrl = client.LogoUri, ClientUrl = client.ClientUri, Description = grant.Description, Created = grant.CreationTime, Expires = grant.Expiration, IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() }; list.Add(item); } } return new GrantsViewModel { Grants = list }; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Grants/GrantsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class GrantsViewModel { public IEnumerable Grants { get; set; } } public class GrantViewModel { public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public string Description { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Home/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ErrorViewModel { public ErrorViewModel() { } public ErrorViewModel(string error) { Error = new ErrorMessage { Error = error }; } public ErrorMessage Error { get; set; } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/Home/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) { _interaction = interaction; _environment = environment; _logger = logger; } public IActionResult Index() { if (_environment.IsDevelopment()) { // only show in development return View(); } _logger.LogInformation("Homepage is disabled in production. Returning 404."); return NotFound(); } /// /// Shows the error page /// public async Task Error(string errorId) { var vm = new ErrorViewModel(); // retrieve error details from identityserver var message = await _interaction.GetErrorContextAsync(errorId); if (message != null) { vm.Error = message; if (!_environment.IsDevelopment()) { // only show in development message.ErrorDescription = null; } } return View("Error", vm); } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/SecurityHeadersAttribute.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class SecurityHeadersAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { var result = context.Result; if (result is ViewResult) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) { context.HttpContext.Response.Headers.Append("X-Content-Type-Options", "nosniff"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) { context.HttpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; // also consider adding upgrade-insecure-requests once you have HTTPS in place for production //csp += "upgrade-insecure-requests;"; // also an example if you need client images to be displayed from twitter // csp += "img-src 'self' https://pbs.twimg.com;"; // once for standards compliant browsers if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("Content-Security-Policy", csp); } // and once again for IE if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("X-Content-Security-Policy", csp); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy var referrer_policy = "no-referrer"; if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) { context.HttpContext.Response.Headers.Append("Referrer-Policy", referrer_policy); } } } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Quickstart/TestUsers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class TestUsers { static readonly object UserAddress = new { street_address = "One Hacker Way", locality = "Heidelberg", postal_code = 69118, country = "Germany" }; public static List Users => new List { new() { SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new (Name, "Alice Smith"), new (GivenName, "Alice"), new (FamilyName, "Smith"), new (Email, "AliceSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://alice.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } }, new() { SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new (Name, "Bob Smith"), new (GivenName, "Bob"), new (FamilyName, "Smith"), new (Email, "BobSmith@email.com"), new (EmailVerified, "true", ClaimValueTypes.Boolean), new (WebSite, "http://bob.com"), new (Address, JsonSerializer.Serialize(UserAddress), IdentityServerClaimValueTypes.Json) } } }; } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Account/AccessDenied.cshtml ================================================ 

    Access Denied

    You do not have access to that resource.

    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Account/LoggedOut.cshtml ================================================ @model LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user ViewData["signed-out"] = true; }

    Logout You are now logged out

    @if (Model.PostLogoutRedirectUri != null) {
    Click here to return to the @Model.ClientName application.
    } @if (Model.SignOutIframeUrl != null) { }
    @section scripts { @if (Model.AutomaticRedirectAfterSignOut) { } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Account/Login.cshtml ================================================ @model LoginViewModel ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Account/Logout.cshtml ================================================ @model LogoutViewModel

    Logout

    Would you like to logout of IdentityServer?

    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Device/Success.cshtml ================================================

    Success

    You have successfully authorized the device

    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Device/UserCodeCapture.cshtml ================================================ @model string

    User Code

    Please enter the code displayed on your device.

    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
    @if (Model.ClientLogoUrl != null) { }

    @Model.ClientName is requesting your permission

    @if (Model.ConfirmUserCode) {

    Please confirm that the authorization request quotes the code: @Model.UserCode.

    }

    Uncheck the permissions you do not wish to grant.

    @if (Model.IdentityScopes.Any()) {
    Personal Information
      @foreach (var scope in Model.IdentityScopes) { }
    } @if (Model.ApiScopes.Any()) {
    Application Access
      @foreach (var scope in Model.ApiScopes) { }
    }
    Description
    @if (Model.AllowRememberConsent) {
    }
    @if (Model.ClientUrl != null) { @Model.ClientName }
    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

    Authentication Cookie

    Claims

    @foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in Model.AuthenticateResult.Properties.Items) {
    @prop.Key
    @prop.Value
    } @if (Model.Clients.Any()) {
    Clients
    @{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
    }
    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

    Client Application Permissions

    Below is the list of applications you have given permission to and the resources they have access to.

    @if (Model.Grants.Any() == false) {
    You have not given access to any applications
    } else { foreach (var grant in Model.Grants) {
    @if (grant.ClientLogoUrl != null) { } @grant.ClientName
      @if (grant.Description != null) {
    • @grant.Description
    • }
    • @grant.Created.ToString("yyyy-MM-dd")
    • @if (grant.Expires.HasValue) {
    • @grant.Expires.Value.ToString("yyyy-MM-dd")
    • } @if (grant.IdentityGrantNames.Any()) {
      • @foreach (var name in grant.IdentityGrantNames) {
      • @name
      • }
    • } @if (grant.ApiGrantNames.Any()) {
      • @foreach (var name in grant.ApiGrantNames) {
      • @name
      • }
    • }
    } }
    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @{ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); }

    Welcome to IdentityServer8 (version @version)

    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

    Error

    Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
    @errorDescription
    } }
    @if (request_id != null) {
    Request Id: @request_id
    }
    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

    You are now being returned to the application

    Once complete, you may close this tab.

    ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
    @RenderBody()
    @RenderSection("scripts", required: false) ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/wwwroot/js/signin-redirect.js ================================================ //window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: samples/Quickstarts/Shared/src/IdentityServer/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Controllers/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class HomeController : Controller { private readonly ILogger _logger; public HomeController(ILogger logger) { _logger = logger; } public IActionResult Index() { return View(); } public async Task CallApi() { var accessToken = await HttpContext.GetTokenAsync("access_token"); var client = new HttpClient(); client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var content = await client.GetStringAsync("https://localhost:6001/identity"); var obj = JsonSerializer.Deserialize(content); ViewBag.Json = obj.ToString(); return View("json"); } public IActionResult Logout() { return SignOut("Cookies", "oidc"); } [ResponseCache(Duration = 0, Location = ResponseCacheLocation.None, NoStore = true)] public IActionResult Error() { return View(new ErrorViewModel { RequestId = Activity.Current?.Id ?? HttpContext.TraceIdentifier }); } } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Mvc; global using System.Diagnostics; global using System.IdentityModel.Tokens.Jwt; global using System.Net.Http.Headers; global using System.Text.Json; ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Models/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ public class ErrorViewModel { public string RequestId { get; set; } public bool ShowRequestId => !string.IsNullOrEmpty(RequestId); } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/MvcClient.csproj ================================================ true PreserveNewest ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ JwtSecurityTokenHandler.DefaultMapInboundClaims = false; var builder = WebApplication.CreateBuilder(args); builder.Services.AddControllersWithViews(); builder.Services .AddAuthentication(options => { options.DefaultScheme = "Cookies"; options.DefaultChallengeScheme = "oidc"; }) .AddCookie("Cookies") .AddOpenIdConnect("oidc", options => { options.Authority = "https://localhost:5001"; options.ClientId = "mvc"; options.ClientSecret = "secret"; options.ResponseType = "code"; options.Scope.Add("api1"); options.SaveTokens = true; }); using (var app = builder.Build()) { if (app.Environment.IsDevelopment()) app.UseDeveloperExceptionPage(); else app.UseExceptionHandler("/Home/Error"); app.UseStaticFiles(); app.UseRouting(); app.UseAuthentication(); app.UseAuthorization(); app.MapDefaultControllerRoute().RequireAuthorization(); await app.RunAsync(); } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Properties/launchSettings.json ================================================ { "profiles": { "MvcClient": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5002" } } } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Views/Home/Index.cshtml ================================================ @using Microsoft.AspNetCore.Authentication

    Claims

    @foreach (var claim in User.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in (await Context.AuthenticateAsync()).Properties.Items) {
    @prop.Key
    @prop.Value
    }
    ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Views/Home/Privacy.cshtml ================================================ @{ ViewData["Title"] = "Privacy Policy"; }

    @ViewData["Title"]

    Use this page to detail your site's privacy policy.

    ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ ViewData["Title"] = "Error"; }

    Error.

    An error occurred while processing your request.

    @if (Model.ShowRequestId) {

    Request ID: @Model.RequestId

    }

    Development Mode

    Swapping to Development environment will display more detailed information about the error that occurred.

    The Development environment shouldn't be enabled for deployed applications. It can result in displaying sensitive information from exceptions to end users. For local debugging, enable the Development environment by setting the ASPNETCORE_ENVIRONMENT environment variable to Development and restarting the app.

    ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Views/Shared/_Layout.cshtml ================================================ @ViewData["Title"] - MvcClient
    @RenderBody()
    @RenderSection("Scripts", required: false) ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Views/Shared/_ValidationScriptsPartial.cshtml ================================================  ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Views/Shared/json.cshtml ================================================
    @ViewBag.Json
    ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Views/_ViewImports.cshtml ================================================ @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/appsettings.Development.json ================================================ { "Logging": { "LogLevel": { "Default": "Debug", "System": "Information", "Microsoft": "Information" } } } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/appsettings.json ================================================ { "Logging": { "LogLevel": { "Default": "Information", "Microsoft": "Warning", "Microsoft.Hosting.Lifetime": "Information" } }, "AllowedHosts": "*" } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/wwwroot/css/site.css ================================================ /* Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification for details on configuring this project to bundle and minify static web assets. */ a.navbar-brand { white-space: normal; text-align: center; word-break: break-all; } /* Provide sufficient contrast against white background */ a { color: #0366d6; } .btn-primary { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } .nav-pills .nav-link.active, .nav-pills .show > .nav-link { color: #fff; background-color: #1b6ec2; border-color: #1861ac; } /* Sticky footer styles -------------------------------------------------- */ html { font-size: 14px; } @media (min-width: 768px) { html { font-size: 16px; } } .border-top { border-top: 1px solid #e5e5e5; } .border-bottom { border-bottom: 1px solid #e5e5e5; } .box-shadow { box-shadow: 0 .25rem .75rem rgba(0, 0, 0, .05); } button.accept-policy { font-size: 1rem; line-height: inherit; } /* Sticky footer styles -------------------------------------------------- */ html { position: relative; min-height: 100%; } body { /* Margin bottom by footer height */ margin-bottom: 60px; } .footer { position: absolute; bottom: 0; width: 100%; white-space: nowrap; line-height: 60px; /* Vertically center the text there */ } ================================================ FILE: samples/Quickstarts/Shared/src/MvcClient/wwwroot/js/site.js ================================================ // Please see documentation at https://docs.microsoft.com/aspnet/core/client-side/bundling-and-minification // for details on configuring this project to bundle and minify static web assets. // Write your JavaScript code. ================================================ FILE: samples/SamplesGlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityModel.Client; global using Serilog; global using System.Diagnostics; global using System.Text; global using System.Text.Json; global using IdentityModel.OidcClient; global using IdentityModel.OidcClient.Browser; global using IdentityModel.AspNetCore.AccessTokenValidation; global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.DataProtection; global using Microsoft.AspNetCore.Hosting; global using Microsoft.AspNetCore.Http; global using Microsoft.Extensions.DependencyInjection; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System.Net; global using System.Net.Sockets; global using System.Runtime.InteropServices; global using System.Security.Cryptography.X509Certificates; global using ILogger = Microsoft.Extensions.Logging.ILogger; global using Microsoft.IdentityModel.Tokens; global using System.IdentityModel.Tokens.Jwt; global using System.Security.Claims; global using System.Runtime.Versioning; ================================================ FILE: src/AspNetIdentity/Directory.Build.props ================================================ ================================================ FILE: src/AspNetIdentity/IdentityServer8.AspNetIdentity.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26228.9 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{932FA9D0-7FB4-4047-8FFD-B74907B5FA15}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.AspNetIdentity", "src\IdentityServer8.AspNetIdentity.csproj", "{A2CDBE15-5898-4A04-94DC-133EE03A78B3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "host\Host.csproj", "{B7FBA07C-A462-4869-AF94-5E36DFC3742D}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "migrations", "migrations", "{4BC142B6-4922-4203-82A2-F28CB6AC5004}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlServer", "migrations\SqlServer\SqlServer.csproj", "{DD44B9E9-C32E-4F89-92C9-25444A709BBB}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A2CDBE15-5898-4A04-94DC-133EE03A78B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2CDBE15-5898-4A04-94DC-133EE03A78B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2CDBE15-5898-4A04-94DC-133EE03A78B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2CDBE15-5898-4A04-94DC-133EE03A78B3}.Release|Any CPU.Build.0 = Release|Any CPU {B7FBA07C-A462-4869-AF94-5E36DFC3742D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B7FBA07C-A462-4869-AF94-5E36DFC3742D}.Debug|Any CPU.Build.0 = Debug|Any CPU {B7FBA07C-A462-4869-AF94-5E36DFC3742D}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7FBA07C-A462-4869-AF94-5E36DFC3742D}.Release|Any CPU.Build.0 = Release|Any CPU {DD44B9E9-C32E-4F89-92C9-25444A709BBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD44B9E9-C32E-4F89-92C9-25444A709BBB}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD44B9E9-C32E-4F89-92C9-25444A709BBB}.Release|Any CPU.ActiveCfg = Release|Any CPU {DD44B9E9-C32E-4F89-92C9-25444A709BBB}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {A2CDBE15-5898-4A04-94DC-133EE03A78B3} = {932FA9D0-7FB4-4047-8FFD-B74907B5FA15} {B7FBA07C-A462-4869-AF94-5E36DFC3742D} = {932FA9D0-7FB4-4047-8FFD-B74907B5FA15} {DD44B9E9-C32E-4F89-92C9-25444A709BBB} = {4BC142B6-4922-4203-82A2-F28CB6AC5004} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {03AF716A-120B-409D-B384-B65E7FDEFEEF} EndGlobalSection EndGlobal ================================================ FILE: src/AspNetIdentity/README.md ================================================ # IdentityServer8.AspNetIdentity ASP.NET Core Identity integration support for IdentityServer8. You can find a detailed walk-through for ASP.NET Core Identity integration [here](https://IdentityServer8.readthedocs.io/en/latest/quickstarts/6_aspnet_identity.html). ## Issues For issues, use the [consolidated IdentityServer8 issue tracker](https://github.com/alexhiggins732/IdentityServer8/issues). ================================================ FILE: src/AspNetIdentity/build.cmd ================================================ @echo off dotnet run --project build -- %* ================================================ FILE: src/AspNetIdentity/build.ps1 ================================================ $ErrorActionPreference = "Stop"; dotnet run --project build -- $args ================================================ FILE: src/AspNetIdentity/build.sh ================================================ #!/usr/bin/env bash set -euo pipefail dotnet run --project build -- "$@" ================================================ FILE: src/AspNetIdentity/host/Configuration/Clients.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Configuration; public static class Clients { public static IEnumerable Get() { var clients = new List(); clients.AddRange(ClientsConsole.Get()); clients.AddRange(ClientsWeb.Get()); return clients; } } ================================================ FILE: src/AspNetIdentity/host/Configuration/ClientsConsole.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Configuration; public static class ClientsConsole { public static IEnumerable Get() { return new List { /////////////////////////////////////////////////////////////// // Console-based Client /////////////////////////////////////////////////////////////// /////////////////////////////////////////// // Console Client Credentials Flow Sample ////////////////////////////////////////// new Client { ClientId = "client", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1", IdentityServerConstants.LocalApi.ScopeName} }, /////////////////////////////////////////// // Console Structured Scope Sample ////////////////////////////////////////// new Client { ClientId = "parameterized.client", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "transaction" } }, /////////////////////////////////////////// // X509 mTLS Client ////////////////////////////////////////// new Client { ClientId = "mtls", ClientSecrets = { // new Secret(@"CN=mtls.test, OU=ROO\ballen@roo, O=mkcert development certificate", "mtls.test") // { // Type = SecretTypes.X509CertificateName // }, new Secret("5D9E9B6B333CD42C99D1DE6175CC0F3EF99DDF68", "mtls.test") { Type = IdentityServerConstants.SecretTypes.X509CertificateThumbprint }, }, AccessTokenType = AccessTokenType.Jwt, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console Client Credentials Flow with client JWT assertion ////////////////////////////////////////// new Client { ClientId = "client.jwt", ClientSecrets = { new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = "MIIEgTCCAumgAwIBAgIQDMMu7l/umJhfEbzJMpcttzANBgkqhkiG9w0BAQsFADCBkzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTQwMgYDVQQLDCtkb21pbmlja0Bkb21icDE2LmZyaXR6LmJveCAoRG9taW5pY2sgQmFpZXIpMTswOQYDVQQDDDJta2NlcnQgZG9taW5pY2tAZG9tYnAxNi5mcml0ei5ib3ggKERvbWluaWNrIEJhaWVyKTAeFw0xOTA2MDEwMDAwMDBaFw0zMDAxMDMxMjM0MDdaMHAxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTE0MDIGA1UECwwrZG9taW5pY2tAZG9tYnAxNi5mcml0ei5ib3ggKERvbWluaWNrIEJhaWVyKTEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNtpipaS8k1zA6w0Aoy8U4l+8zM4jHhhblExf3PULrMR6RauxniTki8p+P8CsZT4V8A4qo+JwsgpLIHrVQrbt9DEhHfBKzxwHqt+GoHt7byTfTtp8A/5nLhYc/5CW4HiR194gVx5+HAlvt+BriMTb1czvTf+H20dj41yUPsN7nMdyRLF+uXapQYMLYnq2BJIDq83mqGwojHk7d+N6GwoO95jlyas7KSoj8/FvfbaqkRNx0446hqPOzFHKc8er8K5VrLp6tVjh8ZJyY0F0dKgx6yWITsL54ctbj/cCyfuGjWEMbS2XXgc+x/xQMnmpfhK1qQAUn9jg5EzF9n6mQomOwIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUEMUlw41YsKZQVls3pEG6CrJk4O8wEQYDVR0RBAowCIIGY2xpZW50MA0GCSqGSIb3DQEBCwUAA4IBgQC0TjNY4Q3Wmw7ggamDImV6HUng3WbYGLYbbL2e3myBrjIxGd1Bi8ZyOu8qeUMIRAbZt2YsSX5S8kx0biaVg2zC+aO5eHhEWMwKB66huInXFjI4wtxZ22r+33fg1R0cLuEUePhftOWrbL0MS4YXVyn9HUMWO4WptG9PJdxNw1UbEB8nw3FkVOdAC9RGqiqalSK+E2UT/kUbTIQ1gPSdQ3nh52mre0H/T9+IRqiozJtNK/CQg4NuEV7rUXHnp7Fmigp6RIJ4TCozglspL341y0rV8M7npU1FYZC2UKNr4ed+GOO1n/sF3LbXDlPXwne99CVVn85wjDaevoR7Md0y2KwE9EggLYcViXNehx4YVv/BjfgqxW8NxiKAxP6kPOZE0XdBrZj2rmcDcGOXCzzYpcduKhFyTOpA0K5RNGC3j1KOUjPVlOtLvjASP7udBEYNfH3mgqXAgqNDOEKi2jG9LITv2IyGUsXhTAsKNJ6A6qiDBzDrvPAYDvsfabPq6tRTwjA=" }, new Secret { Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = "{'e':'AQAB','kid':'ZzAjSnraU3bkWGnnAqLapYGpTyNfLbjbzgAPbbW2GEA','kty':'RSA','n':'wWwQFtSzeRjjerpEM5Rmqz_DsNaZ9S1Bw6UbZkDLowuuTCjBWUax0vBMMxdy6XjEEK4Oq9lKMvx9JzjmeJf1knoqSNrox3Ka0rnxXpNAz6sATvme8p9mTXyp0cX4lF4U2J54xa2_S9NF5QWvpXvBeC4GAJx7QaSw4zrUkrc6XyaAiFnLhQEwKJCwUw4NOqIuYvYp_IXhw-5Ti_icDlZS-282PcccnBeOcX7vc21pozibIdmZJKqXNsL1Ibx5Nkx1F1jLnekJAmdaACDjYRLL_6n3W4wUp19UvzB1lGtXcJKLLkqB6YDiZNu16OSiSprfmrRXvYmvD8m6Fnl5aetgKw'}" } }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Custom Grant Sample ////////////////////////////////////////// new Client { ClientId = "client.custom", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = {"custom", "custom.nosubject"}, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console Resource Owner Flow Sample ////////////////////////////////////////// new Client { ClientId = "roclient", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, "custom.profile", "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console Public Resource Owner Flow Sample ////////////////////////////////////////// new Client { ClientId = "roclient.public", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console with PKCE Sample ////////////////////////////////////////// new Client { ClientId = "console.pkce", ClientName = "Console with PKCE Sample", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RedirectUris = {"http://127.0.0.1"}, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // WinConsole with PKCE Sample ////////////////////////////////////////// new Client { ClientId = "winconsole", ClientName = "Windows Console with PKCE Sample", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RedirectUris = {"sample-windows-client://callback"}, RequireConsent = false, AllowOfflineAccess = true, AllowedIdentityTokenSigningAlgorithms = {"ES256"}, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Introspection Client Sample ////////////////////////////////////////// new Client { ClientId = "roclient.reference", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "resource1.scope1", "resource2.scope1" }, AccessTokenType = AccessTokenType.Reference }, /////////////////////////////////////////// // Device Flow Sample ////////////////////////////////////////// new Client { ClientId = "device", ClientName = "Device Flow Client", AllowedGrantTypes = GrantTypes.DeviceFlow, RequireClientSecret = false, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } } }; } } ================================================ FILE: src/AspNetIdentity/host/Configuration/ClientsWeb.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Configuration; public static class ClientsWeb { static string[] allowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1", "transaction" }; public static IEnumerable Get() { return new List { /////////////////////////////////////////// // JS OIDC Sample ////////////////////////////////////////// new Client { ClientId = "js_oidc", ClientName = "JavaScript OIDC Client", ClientUri = "http://identityserver8.io", AllowedGrantTypes = GrantTypes.Code, RequireClientSecret = false, RedirectUris = { "https://localhost:44300/index.html", "https://localhost:44300/callback.html", "https://localhost:44300/silent.html", "https://localhost:44300/popup.html" }, PostLogoutRedirectUris = { "https://localhost:44300/index.html" }, AllowedCorsOrigins = { "https://localhost:44300" }, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Automatic Token Management Sample ////////////////////////////////////////// new Client { ClientId = "mvc.tokenmanagement", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AccessTokenLifetime = 75, RedirectUris = { "https://localhost:44301/signin-oidc" }, FrontChannelLogoutUri = "https://localhost:44301/signout-oidc", PostLogoutRedirectUris = { "https://localhost:44301/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Code Flow Sample ////////////////////////////////////////// new Client { ClientId = "mvc.code", ClientName = "MVC Code Flow", ClientUri = "http://identityserver8.io", ClientSecrets = { new Secret("secret".Sha256()) }, RequireConsent = true, AllowedGrantTypes = GrantTypes.Code, RedirectUris = { "https://localhost:44302/signin-oidc" }, FrontChannelLogoutUri = "https://localhost:44302/signout-oidc", PostLogoutRedirectUris = { "https://localhost:44302/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Hybrid Flow Sample (Back Channel logout) ////////////////////////////////////////// new Client { ClientId = "mvc.hybrid.backchannel", ClientName = "MVC Hybrid (with BackChannel logout)", ClientUri = "http://identityserver8.io", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Hybrid, RequirePkce = false, RedirectUris = { "https://localhost:44303/signin-oidc" }, BackChannelLogoutUri = "https://localhost:44303/logout", PostLogoutRedirectUris = { "https://localhost:44303/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes } }; } } ================================================ FILE: src/AspNetIdentity/host/Configuration/Resources.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Configuration; public class Resources { // identity resources represent identity data about a user that can be requested via the scope parameter (OpenID Connect) public static readonly IEnumerable IdentityResources = new[] { // some standard scopes from the OIDC spec new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email(), // custom identity resource with some consolidated claims new IdentityResource("custom.profile", new[] { JwtClaimTypes.Name, JwtClaimTypes.Email, "location" }) }; // API scopes represent values that describe scope of access and can be requested by the scope parameter (OAuth) public static readonly IEnumerable ApiScopes = new[] { // local API scope new ApiScope(LocalApi.ScopeName), // resource specific scopes new ApiScope("resource1.scope1"), new ApiScope("resource2.scope1"), // a scope without resource association new ApiScope("scope3"), // a scope shared by multiple resources new ApiScope("shared.scope"), // a parameterized scope new ApiScope("transaction", "Transaction") { Description = "Some Transaction" } }; // API resources are more formal representation of a resource with processing rules and their scopes (if any) public static readonly IEnumerable ApiResources = new[] { new ApiResource("resource1", "Resource 1") { ApiSecrets = { new Secret("secret".Sha256()) }, Scopes = { "resource1.scope1", "shared.scope" } }, // expanded version if more control is needed new ApiResource("resource2", "Resource 2") { ApiSecrets = { new Secret("secret".Sha256()) }, // additional claims to put into access token UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Email }, Scopes = { "resource2.scope1", "shared.scope" } } }; } ================================================ FILE: src/AspNetIdentity/host/Data/ApplicationDbContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Data; public class ApplicationDbContext : IdentityDbContext { public ApplicationDbContext(DbContextOptions options) : base(options) { } protected override void OnModelCreating(ModelBuilder builder) { base.OnModelCreating(builder); // Customize the ASP.NET Identity model and override the defaults if needed. // For example, you can rename the ASP.NET Identity table names and more. // Add your customizations after calling base.OnModelCreating(builder); } } ================================================ FILE: src/AspNetIdentity/host/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using IdentityServerHost.Configuration; global using IdentityServerHost.Data; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Identity; global using Microsoft.AspNetCore.Identity.EntityFrameworkCore; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.EntityFrameworkCore; global using Microsoft.Extensions.Options; global using Newtonsoft.Json; global using Serilog; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System.ComponentModel; global using System.ComponentModel.DataAnnotations; global using System.Diagnostics; global using System.Security.Claims; global using System.Text; global using static IdentityServer8.IdentityServerConstants; global using ILogger = Microsoft.Extensions.Logging.ILogger; global using ClaimValueTypes = System.Security.Claims.ClaimValueTypes; ================================================ FILE: src/AspNetIdentity/host/Host.csproj ================================================ ================================================ FILE: src/AspNetIdentity/host/Models/AccountViewModels/ExternalLoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.AccountViewModels; public class ExternalLoginViewModel { [Required] [EmailAddress] public string Email { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/AccountViewModels/ForgotPasswordViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.AccountViewModels; public class ForgotPasswordViewModel { [Required] [EmailAddress] public string Email { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/AccountViewModels/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.AccountViewModels; public class LoginViewModel { [Required] [EmailAddress] public string Email { get; set; } [Required] [DataType(DataType.Password)] public string Password { get; set; } [Display(Name = "Remember me?")] public bool RememberMe { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/AccountViewModels/LoginWith2faViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.AccountViewModels; public class LoginWith2faViewModel { [Required] [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Text)] [Display(Name = "Authenticator code")] public string TwoFactorCode { get; set; } [Display(Name = "Remember this machine")] public bool RememberMachine { get; set; } public bool RememberMe { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/AccountViewModels/LoginWithRecoveryCodeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.AccountViewModels; public class LoginWithRecoveryCodeViewModel { [Required] [DataType(DataType.Text)] [Display(Name = "Recovery Code")] public string RecoveryCode { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/AccountViewModels/RegisterViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.AccountViewModels; public class RegisterViewModel { [Required] [EmailAddress] [Display(Name = "Email")] public string Email { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "Password")] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/AccountViewModels/ResetPasswordViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.AccountViewModels; public class ResetPasswordViewModel { [Required] [EmailAddress] public string Email { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] public string Password { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm password")] [Compare("Password", ErrorMessage = "The password and confirmation password do not match.")] public string ConfirmPassword { get; set; } public string Code { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/ApplicationUser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; // Add profile data for application users by adding properties to the ApplicationUser class public class ApplicationUser : IdentityUser { } ================================================ FILE: src/AspNetIdentity/host/Models/ManageViewModels/ChangePasswordViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.ManageViewModels; public class ChangePasswordViewModel { [Required] [DataType(DataType.Password)] [Display(Name = "Current password")] public string OldPassword { get; set; } [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } public string StatusMessage { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/ManageViewModels/EnableAuthenticatorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.ManageViewModels; public class EnableAuthenticatorViewModel { [Required] [StringLength(7, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Text)] [Display(Name = "Verification Code")] public string Code { get; set; } [ReadOnly(true)] public string SharedKey { get; set; } public string AuthenticatorUri { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/ManageViewModels/ExternalLoginsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.ManageViewModels; public class ExternalLoginsViewModel { public IList CurrentLogins { get; set; } public IList OtherLogins { get; set; } public bool ShowRemoveButton { get; set; } public string StatusMessage { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/ManageViewModels/GenerateRecoveryCodesViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.ManageViewModels; public class GenerateRecoveryCodesViewModel { public string[] RecoveryCodes { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/ManageViewModels/IndexViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.ManageViewModels; public class IndexViewModel { public string Username { get; set; } public bool IsEmailConfirmed { get; set; } [Required] [EmailAddress] public string Email { get; set; } [Phone] [Display(Name = "Phone number")] public string PhoneNumber { get; set; } public string StatusMessage { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/ManageViewModels/RemoveLoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.ManageViewModels; public class RemoveLoginViewModel { public string LoginProvider { get; set; } public string ProviderKey { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/ManageViewModels/SetPasswordViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.ManageViewModels; public class SetPasswordViewModel { [Required] [StringLength(100, ErrorMessage = "The {0} must be at least {2} and at max {1} characters long.", MinimumLength = 6)] [DataType(DataType.Password)] [Display(Name = "New password")] public string NewPassword { get; set; } [DataType(DataType.Password)] [Display(Name = "Confirm new password")] [Compare("NewPassword", ErrorMessage = "The new password and confirmation password do not match.")] public string ConfirmPassword { get; set; } public string StatusMessage { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Models/ManageViewModels/TwoFactorAuthenticationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models.ManageViewModels; public class TwoFactorAuthenticationViewModel { public bool HasAuthenticator { get; set; } public int RecoveryCodesLeft { get; set; } public bool Is2faEnabled { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost; public class Program { public static int Main(string[] args) { Console.Title = "IdentityServer8.AspNetIdentity"; Activity.DefaultIdFormat = ActivityIdFormat.W3C; Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() //.WriteTo.File(@"IdentityServer8_log.txt") // uncomment to write to Azure diagnostics stream //.WriteTo.File( // @"D:\home\LogFiles\Application\identityserver.txt", // fileSizeLimitBytes: 1_000_000, // rollOnFileSizeLimit: true, // shared: true, // flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) .CreateLogger(); try { Log.Information("Starting host..."); CreateHostBuilder(args).Build().Run(); return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSerilog() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } ================================================ FILE: src/AspNetIdentity/host/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": true, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:5000", "sslPort": 44334 } }, "profiles": { "Host": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001" } } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/AccountController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly UserManager _userManager; private readonly SignInManager _signInManager; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController( UserManager userManager, SignInManager signInManager, IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events) { _userManager = userManager; _signInManager = signInManager; _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; } /// /// Entry point into the login workflow /// [HttpGet] public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); if (vm.IsExternalLoginOnly) { // we only have one option for logging in and it's an external provider return returnUrl.IsAllowedRedirect() ? RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl = returnUrl.SanitizeForRedirect() }) : Forbid(); } return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); // check if we are in the context of an authorization request if (ModelState.IsValid) { var result = await _signInManager.PasswordSignInAsync(model.Username, model.Password, model.RememberLogin, lockoutOnFailure: true); if (result.Succeeded) { var user = await _userManager.FindByNameAsync(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.UserName, user.Id, user.UserName, clientId: context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", model.ReturnUrl); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return Redirect("~/"); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task LoginCancel(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl) : Forbid(); } return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else { // since we don't have a valid context, then we just go back to the home page return Redirect("~/"); } } /// /// Show logout page /// [HttpGet] public async Task Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); } /// /// Handle logout page postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await _signInManager.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); } [HttpGet] public IActionResult AccessDenied() { return View(); } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) { var local = context.IdP == IdentityServer8.IdentityServerConstants.LocalIdentityProvider; // this is meant to short circuit the UI and only trigger the one external IdP var vm = new LoginViewModel { EnableLocalLogin = local, ReturnUrl = returnUrl, Username = context?.LoginHint, }; if (!local) { vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; } return vm; } var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes .Where(x => x.DisplayName != null) .Select(x => new ExternalProvider { DisplayName = x.DisplayName ?? x.Name, AuthenticationScheme = x.Name }).ToList(); var allowLocal = true; if (context?.Client.ClientId != null) { var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); if (client != null) { allowLocal = client.EnableLocalLogin; if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) { providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); } } } return new LoginViewModel { AllowRememberLogin = AccountOptions.AllowRememberLogin, EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, ReturnUrl = returnUrl, Username = context?.LoginHint, ExternalProviders = providers.ToArray() }; } private async Task BuildLoginViewModelAsync(LoginInputModel model) { var vm = await BuildLoginViewModelAsync(model.ReturnUrl); vm.Username = model.Username; vm.RememberLogin = model.RememberLogin; return vm; } private async Task BuildLogoutViewModelAsync(string logoutId) { var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; if (User?.Identity.IsAuthenticated != true) { // if the user is not authenticated, then just show logged out page vm.ShowLogoutPrompt = false; return vm; } var context = await _interaction.GetLogoutContextAsync(logoutId); if (context?.ShowSignoutPrompt == false) { // it's safe to automatically sign-out vm.ShowLogoutPrompt = false; return vm; } // show the logout prompt. this prevents attacks where the user // is automatically signed out by another malicious web page. return vm; } private async Task BuildLoggedOutViewModelAsync(string logoutId) { // get context information (client name, post logout redirect URI and iframe for federated signout) var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LoggedOutViewModel { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; if (User?.Identity.IsAuthenticated == true) { var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; if (idp != null && idp != IdentityServer8.IdentityServerConstants.LocalIdentityProvider) { var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); if (providerSupportsSignout) { if (vm.LogoutId == null) { // if there's no current logout context, we need to create one // this captures necessary info from the current logged in user // before we signout and redirect away to the external IdP for signout vm.LogoutId = await _interaction.CreateLogoutContextAsync(); } vm.ExternalAuthenticationScheme = idp; } } } return vm; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/AccountOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/ExternalController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { private readonly UserManager _userManager; private readonly SignInManager _signInManager; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IEventService _events; private readonly ILogger _logger; public ExternalController( UserManager userManager, SignInManager signInManager, IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService events, ILogger logger) { _userManager = userManager; _signInManager = signInManager; _interaction = interaction; _clientStore = clientStore; _events = events; _logger = logger; } /// /// initiate roundtrip to external authentication provider /// [HttpGet] public IActionResult Challenge(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; // validate returnUrl - either it is a valid OIDC URL or back to a local page if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } // start challenge and roundtrip the return URL and scheme var props = new AuthenticationProperties { RedirectUri = Url.Action(nameof(Callback)), Items = { { "returnUrl", returnUrl }, { "scheme", scheme }, } }; return Challenge(props, scheme); } /// /// Post processing of external authentication /// [HttpGet] public async Task Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = await FindUserFromExternalProviderAsync(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = await AutoProvisionUserAsync(provider, providerUserId, claims); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user // we must issue the cookie maually, and can't use the SignInManager because // it doesn't expose an API to issue additional claims from the login workflow var principal = await _signInManager.CreateUserPrincipalAsync(user); additionalLocalClaims.AddRange(principal.Claims); var name = principal.FindFirst(JwtClaimTypes.Name)?.Value ?? user.Id; var isuser = new IdentityServerUser(user.Id) { DisplayName = name, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.Id, name, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } private async Task<(ApplicationUser user, string provider, string providerUserId, IEnumerable claims)> FindUserFromExternalProviderAsync(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = await _userManager.FindByLoginAsync(provider, providerUserId); return (user, provider, providerUserId, claims); } private async Task AutoProvisionUserAsync(string provider, string providerUserId, IEnumerable claims) { // create a list of claims that we want to transfer into our store var filtered = new List(); // user's display name var name = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Name)?.Value ?? claims.FirstOrDefault(x => x.Type == ClaimTypes.Name)?.Value; if (name != null) { filtered.Add(new Claim(JwtClaimTypes.Name, name)); } else { var first = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.GivenName)?.Value ?? claims.FirstOrDefault(x => x.Type == ClaimTypes.GivenName)?.Value; var last = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.FamilyName)?.Value ?? claims.FirstOrDefault(x => x.Type == ClaimTypes.Surname)?.Value; if (first != null && last != null) { filtered.Add(new Claim(JwtClaimTypes.Name, first + " " + last)); } else if (first != null) { filtered.Add(new Claim(JwtClaimTypes.Name, first)); } else if (last != null) { filtered.Add(new Claim(JwtClaimTypes.Name, last)); } } // email var email = claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Email)?.Value ?? claims.FirstOrDefault(x => x.Type == ClaimTypes.Email)?.Value; if (email != null) { filtered.Add(new Claim(JwtClaimTypes.Email, email)); } var user = new ApplicationUser { UserName = Guid.NewGuid().ToString(), }; var identityResult = await _userManager.CreateAsync(user); if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); if (filtered.Any()) { identityResult = await _userManager.AddClaimsAsync(user, filtered); if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); } identityResult = await _userManager.AddLoginAsync(user, new UserLoginInfo(provider, providerUserId, provider)); if (!identityResult.Succeeded) throw new Exception(identityResult.Errors.First().Description); return user; } // if the external login is OIDC-based, there are certain things we need to preserve to make logout work // this will be different for WS-Fed, SAML2p or other protocols private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) { // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout var idToken = externalResult.Properties.GetTokenValue("id_token"); if (idToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); } } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/ExternalProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ExternalProvider { public string DisplayName { get; set; } public string AuthenticationScheme { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/LoggedOutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoggedOutViewModel { public string PostLogoutRedirectUri { get; set; } public string ClientName { get; set; } public string SignOutIframeUrl { get; set; } public bool AutomaticRedirectAfterSignOut { get; set; } public string LogoutId { get; set; } public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; public string ExternalAuthenticationScheme { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/LoginInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginInputModel { [Required] public string Username { get; set; } [Required] public string Password { get; set; } public bool RememberLogin { get; set; } public string ReturnUrl { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginViewModel : LoginInputModel { public bool AllowRememberLogin { get; set; } = true; public bool EnableLocalLogin { get; set; } = true; public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/LogoutInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutInputModel { public string LogoutId { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/LogoutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutViewModel : LogoutInputModel { public bool ShowLogoutPrompt { get; set; } = true; } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Account/RedirectViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class RedirectViewModel { public string RedirectUrl { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Consent/ConsentController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This controller processes the consent UI /// [SecurityHeaders] [Authorize] public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IEventService _events; private readonly ILogger _logger; public ConsentController( IIdentityServerInteractionService interaction, IEventService events, ILogger logger) { _interaction = interaction; _events = events; _logger = logger; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { var result = await ProcessConsent(model); if (result.IsRedirect) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context?.IsNativeClient() == true) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", result.RedirectUri); } return result.RedirectUri.IsAllowedRedirect() ? Redirect(result.RedirectUri.SanitizeForRedirect()) : Forbid(); } if (result.HasValidationError) { ModelState.AddModelError(string.Empty, result.ValidationError); } if (result.ShowView) { return View("Index", result.ViewModel); } return View("Error"); } /*****************************************/ /* helper APIs for the ConsentController */ /*****************************************/ private async Task ProcessConsent(ConsentInputModel model) { var result = new ProcessConsentResult(); // validate return url is still valid var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model?.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model?.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); } return result; } private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { return CreateConsentViewModel(model, returnUrl, request); } else { _logger.LogError("No consent request matching request: {0}", Ioc.Sanitizer.Log.Sanitize(returnUrl)); } return null; } private ConsentViewModel CreateConsentViewModel( ConsentInputModel model, string returnUrl, AuthorizationRequest request) { var vm = new ConsentViewModel { RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), Description = model?.Description, ReturnUrl = returnUrl, ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { var displayName = apiScope.DisplayName ?? apiScope.Name; if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) { displayName += ":" + parsedScopeValue.ParsedParameter; } return new ScopeViewModel { Value = parsedScopeValue.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Consent/ConsentInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentInputModel { public string Button { get; set; } public IEnumerable ScopesConsented { get; set; } public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Consent/ConsentOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentOptions { public static bool EnableOfflineAccess = true; public static string OfflineAccessDisplayName = "Offline Access"; public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Consent/ConsentViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentViewModel : ConsentInputModel { public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public bool AllowRememberConsent { get; set; } public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Consent/ProcessConsentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ProcessConsentResult { public bool IsRedirect => RedirectUri != null; public string RedirectUri { get; set; } public Client Client { get; set; } public bool ShowView => ViewModel != null; public ConsentViewModel ViewModel { get; set; } public bool HasValidationError => ValidationError != null; public string ValidationError { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Consent/ScopeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ScopeViewModel { public string Value { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Emphasize { get; set; } public bool Required { get; set; } public bool Checked { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Device/DeviceAuthorizationInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Device/DeviceAuthorizationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationViewModel : ConsentViewModel { public string UserCode { get; set; } public bool ConfirmUserCode { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Device/DeviceController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [Authorize] [SecurityHeaders] public class DeviceController : Controller { private readonly IDeviceFlowInteractionService _interaction; private readonly IEventService _events; private readonly IOptions _options; private readonly ILogger _logger; public DeviceController( IDeviceFlowInteractionService interaction, IEventService eventService, IOptions options, ILogger logger) { _interaction = interaction; _events = eventService; _options = options; _logger = logger; } [HttpGet] public async Task Index() { string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; string userCode = Request.Query[userCodeParamName]; if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); vm.ConfirmUserCode = true; return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task UserCodeCapture(string userCode) { var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task Callback(DeviceAuthorizationInputModel model) { if (model == null) throw new ArgumentNullException(nameof(model)); var result = await ProcessConsent(model); if (result.HasValidationError) return View("Error"); return View("Success"); } private async Task ProcessConsent(DeviceAuthorizationInputModel model) { var result = new ProcessConsentResult(); var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.UserCode, model); } return result; } private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(userCode); if (request != null) { return CreateConsentViewModel(userCode, model, request); } return null; } private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { UserCode = userCode, Description = model?.Description, RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? DisplayName = apiScope.DisplayName ?? apiScope.Name, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Diagnostics/DiagnosticsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [Authorize] public class DiagnosticsController : Controller { public async Task Index() { var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) { return NotFound(); } var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); return View(model); } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Diagnostics/DiagnosticsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DiagnosticsViewModel { public DiagnosticsViewModel(AuthenticateResult result) { AuthenticateResult = result; if (result.Properties.Items.ContainsKey("client_list")) { var encoded = result.Properties.Items["client_list"]; var bytes = Base64Url.Decode(encoded); var value = Encoding.UTF8.GetString(bytes); Clients = JsonConvert.DeserializeObject(value); } } public AuthenticateResult AuthenticateResult { get; } public IEnumerable Clients { get; } = new List(); } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public static class Extensions { /// /// Checks if the redirect URI is for a native client. /// /// public static bool IsNativeClient(this AuthorizationRequest context) { return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); } public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) { controller.HttpContext.Response.StatusCode = 200; controller.HttpContext.Response.Headers["Location"] = ""; return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Grants/GrantsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller allows a user to revoke grants given to clients /// [SecurityHeaders] [Authorize] public class GrantsController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; private readonly IResourceStore _resources; private readonly IEventService _events; public GrantsController(IIdentityServerInteractionService interaction, IClientStore clients, IResourceStore resources, IEventService events) { _interaction = interaction; _clients = clients; _resources = resources; _events = events; } /// /// Show list of grants /// [HttpGet] public async Task Index() { return View("Index", await BuildViewModelAsync()); } /// /// Handle postback to revoke a client /// [HttpPost] [ValidateAntiForgeryToken] public async Task Revoke(string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return RedirectToAction("Index"); } private async Task BuildViewModelAsync() { var grants = await _interaction.GetAllUserGrantsAsync(); var list = new List(); foreach(var grant in grants) { var client = await _clients.FindClientByIdAsync(grant.ClientId); if (client != null) { var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); var item = new GrantViewModel() { ClientId = client.ClientId, ClientName = client.ClientName ?? client.ClientId, ClientLogoUrl = client.LogoUri, ClientUrl = client.ClientUri, Description = grant.Description, Created = grant.CreationTime, Expires = grant.Expiration, IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() }; list.Add(item); } } return new GrantsViewModel { Grants = list }; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Grants/GrantsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class GrantsViewModel { public IEnumerable Grants { get; set; } } public class GrantViewModel { public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public string Description { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Home/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ErrorViewModel { public ErrorViewModel() { } public ErrorViewModel(string error) { Error = new ErrorMessage { Error = error }; } public ErrorMessage Error { get; set; } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/Home/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) { _interaction = interaction; _environment = environment; _logger = logger; } public IActionResult Index() { if (_environment.IsDevelopment()) { // only show in development return View(); } _logger.LogInformation("Homepage is disabled in production. Returning 404."); return NotFound(); } /// /// Shows the error page /// public async Task Error(string errorId) { var vm = new ErrorViewModel(); // retrieve error details from identityserver var message = await _interaction.GetErrorContextAsync(errorId); if (message != null) { vm.Error = message; if (!_environment.IsDevelopment()) { // only show in development message.ErrorDescription = null; } } return View("Error", vm); } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/SecurityHeadersAttribute.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class SecurityHeadersAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { var result = context.Result; if (result is ViewResult) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) { context.HttpContext.Response.Headers.Append("X-Content-Type-Options", "nosniff"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) { context.HttpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; // also consider adding upgrade-insecure-requests once you have HTTPS in place for production //csp += "upgrade-insecure-requests;"; // also an example if you need client images to be displayed from twitter // csp += "img-src 'self' https://pbs.twimg.com;"; // once for standards compliant browsers if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("Content-Security-Policy", csp); } // and once again for IE if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("X-Content-Security-Policy", csp); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy var referrer_policy = "no-referrer"; if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) { context.HttpContext.Response.Headers.Append("Referrer-Policy", referrer_policy); } } } } ================================================ FILE: src/AspNetIdentity/host/Quickstart/TestUsers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class TestUsers { public static List Users = new List { new TestUser{SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new Claim(JwtClaimTypes.Name, "Alice Smith"), new Claim(JwtClaimTypes.GivenName, "Alice"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://alice.com"), new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer8.IdentityServerConstants.ClaimValueTypes.Json) } }, new TestUser{SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer8.IdentityServerConstants.ClaimValueTypes.Json), new Claim("location", "somewhere") } } }; } ================================================ FILE: src/AspNetIdentity/host/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost { public class Startup { public Startup(IConfiguration configuration) { Configuration = configuration; } public IConfiguration Configuration { get; } public void ConfigureServices(IServiceCollection services) { services.AddDbContext(options => options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); services.AddControllersWithViews(); services.AddIdentityServer() .AddDeveloperSigningCredential() .AddInMemoryIdentityResources(IdentityServerHost.Configuration.Resources.IdentityResources) .AddInMemoryApiResources(IdentityServerHost.Configuration.Resources.ApiResources) .AddInMemoryApiScopes(IdentityServerHost.Configuration.Resources.ApiScopes) .AddInMemoryClients(Clients.Get()) .AddAspNetIdentity(); services.AddAuthentication() .AddOpenIdConnect("Google", "Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ForwardSignOut = IdentityServerConstants.DefaultCookieAuthenticationScheme; options.Authority = "https://accounts.google.com/"; options.ClientId = "708996912208-9m4dkjb5hscn7cjrn5u0r4tbgkbj1fko.apps.googleusercontent.com"; options.CallbackPath = "/signin-google"; options.Scope.Add("email"); }); } public void Configure(IApplicationBuilder app, IWebHostEnvironment env) { if (env.IsDevelopment()) { app.UseDeveloperExceptionPage(); //app.UseDatabaseErrorPage(); } else { app.UseExceptionHandler("/Home/Error"); } app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } } } ================================================ FILE: src/AspNetIdentity/host/Views/Account/AccessDenied.cshtml ================================================ 

    Access Denied

    You do not have access to that resource.

    ================================================ FILE: src/AspNetIdentity/host/Views/Account/LoggedOut.cshtml ================================================ @model LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user ViewData["signed-out"] = true; }

    Logout You are now logged out

    @if (Model.PostLogoutRedirectUri != null) {
    Click here to return to the @Model.ClientName application.
    } @if (Model.SignOutIframeUrl != null) { }
    @section scripts { @if (Model.AutomaticRedirectAfterSignOut) { } } ================================================ FILE: src/AspNetIdentity/host/Views/Account/Login.cshtml ================================================ @model LoginViewModel ================================================ FILE: src/AspNetIdentity/host/Views/Account/Logout.cshtml ================================================ @model LogoutViewModel

    Logout

    Would you like to logout of IdentityServer?

    ================================================ FILE: src/AspNetIdentity/host/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: src/AspNetIdentity/host/Views/Device/Success.cshtml ================================================

    Success

    You have successfully authorized the device

    ================================================ FILE: src/AspNetIdentity/host/Views/Device/UserCodeCapture.cshtml ================================================ @model string

    User Code

    Please enter the code displayed on your device.

    ================================================ FILE: src/AspNetIdentity/host/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
    @if (Model.ClientLogoUrl != null) { }

    @Model.ClientName is requesting your permission

    @if (Model.ConfirmUserCode) {

    Please confirm that the authorization request quotes the code: @Model.UserCode.

    }

    Uncheck the permissions you do not wish to grant.

    @if (Model.IdentityScopes.Any()) {
    Personal Information
      @foreach (var scope in Model.IdentityScopes) { }
    } @if (Model.ApiScopes.Any()) {
    Application Access
      @foreach (var scope in Model.ApiScopes) { }
    }
    Description
    @if (Model.AllowRememberConsent) {
    }
    @if (Model.ClientUrl != null) { @Model.ClientName }
    ================================================ FILE: src/AspNetIdentity/host/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

    Authentication Cookie

    Claims

    @foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in Model.AuthenticateResult.Properties.Items) {
    @prop.Key
    @prop.Value
    } @if (Model.Clients.Any()) {
    Clients
    @{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
    }
    ================================================ FILE: src/AspNetIdentity/host/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

    Client Application Permissions

    Below is the list of applications you have given permission to and the resources they have access to.

    @if (Model.Grants.Any() == false) {
    You have not given access to any applications
    } else { foreach (var grant in Model.Grants) {
    @if (grant.ClientLogoUrl != null) { } @grant.ClientName
      @if (grant.Description != null) {
    • @grant.Description
    • }
    • @grant.Created.ToString("yyyy-MM-dd")
    • @if (grant.Expires.HasValue) {
    • @grant.Expires.Value.ToString("yyyy-MM-dd")
    • } @if (grant.IdentityGrantNames.Any()) {
      • @foreach (var name in grant.IdentityGrantNames) {
      • @name
      • }
    • } @if (grant.ApiGrantNames.Any()) {
      • @foreach (var name in grant.ApiGrantNames) {
      • @name
      • }
    • }
    } }
    ================================================ FILE: src/AspNetIdentity/host/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @{ var version = FileVersionInfo.GetVersionInfo(typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.Location).ProductVersion.Split('+').First(); }

    Welcome to IdentityServer8 (version @version)

    ================================================ FILE: src/AspNetIdentity/host/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

    Error

    Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
    @errorDescription
    } }
    @if (request_id != null) {
    Request Id: @request_id
    }
    ================================================ FILE: src/AspNetIdentity/host/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

    You are now being returned to the application

    Once complete, you may close this tab.

    ================================================ FILE: src/AspNetIdentity/host/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
    @RenderBody()
    @RenderSection("scripts", required: false) ================================================ FILE: src/AspNetIdentity/host/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: src/AspNetIdentity/host/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: src/AspNetIdentity/host/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: src/AspNetIdentity/host/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: src/AspNetIdentity/host/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: src/AspNetIdentity/host/appsettings.json ================================================ { "ConnectionStrings": { "DefaultConnection": "Server=(localdb)\\mssqllocaldb;Database=IdentityServer8.AspNetIdentity-8.0.0;Trusted_Connection=True;MultipleActiveResultSets=true" }, "Logging": { "IncludeScopes": false, "LogLevel": { "Default": "Warning" } } } ================================================ FILE: src/AspNetIdentity/host/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: src/AspNetIdentity/host/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: src/AspNetIdentity/host/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: src/AspNetIdentity/host/wwwroot/js/signin-redirect.js ================================================ //window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: src/AspNetIdentity/host/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8.Models; global using IdentityServerHost; global using IdentityServerHost.Data; global using Microsoft.AspNetCore; global using Microsoft.AspNetCore.Identity; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.Infrastructure; global using Microsoft.EntityFrameworkCore.Metadata; global using Microsoft.EntityFrameworkCore.Migrations; global using System.Security.Claims; ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/Migrations/UsersDb/20200323135751_Users.Designer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // namespace SqlServer.Migrations.UsersDb { [DbContext(typeof(ApplicationDbContext))] [Migration("20200323135751_Users")] partial class Users { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.0") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.Models.ApplicationUser", b => { b.Property("Id") .HasColumnType("nvarchar(450)"); b.Property("AccessFailedCount") .HasColumnType("int"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnType("nvarchar(max)"); b.Property("Email") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.Property("EmailConfirmed") .HasColumnType("bit"); b.Property("LockoutEnabled") .HasColumnType("bit"); b.Property("LockoutEnd") .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.Property("NormalizedUserName") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); b.Property("PhoneNumber") .HasColumnType("nvarchar(max)"); b.Property("PhoneNumberConfirmed") .HasColumnType("bit"); b.Property("SecurityStamp") .HasColumnType("nvarchar(max)"); b.Property("TwoFactorEnabled") .HasColumnType("bit"); b.Property("UserName") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.HasKey("Id"); b.HasIndex("NormalizedEmail") .HasName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() .HasName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); b.ToTable("AspNetUsers"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { b.Property("Id") .HasColumnType("nvarchar(450)"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnType("nvarchar(max)"); b.Property("Name") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.Property("NormalizedName") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() .HasName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); b.ToTable("AspNetRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); b.Property("ClaimValue") .HasColumnType("nvarchar(max)"); b.Property("RoleId") .IsRequired() .HasColumnType("nvarchar(450)"); b.HasKey("Id"); b.HasIndex("RoleId"); b.ToTable("AspNetRoleClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); b.Property("ClaimValue") .HasColumnType("nvarchar(max)"); b.Property("UserId") .IsRequired() .HasColumnType("nvarchar(450)"); b.HasKey("Id"); b.HasIndex("UserId"); b.ToTable("AspNetUserClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") .HasColumnType("nvarchar(450)"); b.Property("ProviderKey") .HasColumnType("nvarchar(450)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); b.Property("UserId") .IsRequired() .HasColumnType("nvarchar(450)"); b.HasKey("LoginProvider", "ProviderKey"); b.HasIndex("UserId"); b.ToTable("AspNetUserLogins"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.Property("UserId") .HasColumnType("nvarchar(450)"); b.Property("RoleId") .HasColumnType("nvarchar(450)"); b.HasKey("UserId", "RoleId"); b.HasIndex("RoleId"); b.ToTable("AspNetUserRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.Property("UserId") .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") .HasColumnType("nvarchar(450)"); b.Property("Name") .HasColumnType("nvarchar(450)"); b.Property("Value") .HasColumnType("nvarchar(max)"); b.HasKey("UserId", "LoginProvider", "Name"); b.ToTable("AspNetUserTokens"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.HasOne("IdentityServer8.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.HasOne("IdentityServer8.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.HasOne("IdentityServer8.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.HasOne("IdentityServer8.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); #pragma warning restore 612, 618 } } } ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/Migrations/UsersDb/20200323135751_Users.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace SqlServer.Migrations.UsersDb; public partial class Users : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "AspNetRoles", columns: table => new { Id = table.Column(nullable: false), Name = table.Column(maxLength: 256, nullable: true), NormalizedName = table.Column(maxLength: 256, nullable: true), ConcurrencyStamp = table.Column(nullable: true) }, constraints: table => { table.PrimaryKey("PK_AspNetRoles", x => x.Id); }); migrationBuilder.CreateTable( name: "AspNetUsers", columns: table => new { Id = table.Column(nullable: false), UserName = table.Column(maxLength: 256, nullable: true), NormalizedUserName = table.Column(maxLength: 256, nullable: true), Email = table.Column(maxLength: 256, nullable: true), NormalizedEmail = table.Column(maxLength: 256, nullable: true), EmailConfirmed = table.Column(nullable: false), PasswordHash = table.Column(nullable: true), SecurityStamp = table.Column(nullable: true), ConcurrencyStamp = table.Column(nullable: true), PhoneNumber = table.Column(nullable: true), PhoneNumberConfirmed = table.Column(nullable: false), TwoFactorEnabled = table.Column(nullable: false), LockoutEnd = table.Column(nullable: true), LockoutEnabled = table.Column(nullable: false), AccessFailedCount = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_AspNetUsers", x => x.Id); }); migrationBuilder.CreateTable( name: "AspNetRoleClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), RoleId = table.Column(nullable: false), ClaimType = table.Column(nullable: true), ClaimValue = table.Column(nullable: true) }, constraints: table => { table.PrimaryKey("PK_AspNetRoleClaims", x => x.Id); table.ForeignKey( name: "FK_AspNetRoleClaims_AspNetRoles_RoleId", column: x => x.RoleId, principalTable: "AspNetRoles", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "AspNetUserClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), UserId = table.Column(nullable: false), ClaimType = table.Column(nullable: true), ClaimValue = table.Column(nullable: true) }, constraints: table => { table.PrimaryKey("PK_AspNetUserClaims", x => x.Id); table.ForeignKey( name: "FK_AspNetUserClaims_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "AspNetUserLogins", columns: table => new { LoginProvider = table.Column(nullable: false), ProviderKey = table.Column(nullable: false), ProviderDisplayName = table.Column(nullable: true), UserId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_AspNetUserLogins", x => new { x.LoginProvider, x.ProviderKey }); table.ForeignKey( name: "FK_AspNetUserLogins_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "AspNetUserRoles", columns: table => new { UserId = table.Column(nullable: false), RoleId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_AspNetUserRoles", x => new { x.UserId, x.RoleId }); table.ForeignKey( name: "FK_AspNetUserRoles_AspNetRoles_RoleId", column: x => x.RoleId, principalTable: "AspNetRoles", principalColumn: "Id", onDelete: ReferentialAction.Cascade); table.ForeignKey( name: "FK_AspNetUserRoles_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "AspNetUserTokens", columns: table => new { UserId = table.Column(nullable: false), LoginProvider = table.Column(nullable: false), Name = table.Column(nullable: false), Value = table.Column(nullable: true) }, constraints: table => { table.PrimaryKey("PK_AspNetUserTokens", x => new { x.UserId, x.LoginProvider, x.Name }); table.ForeignKey( name: "FK_AspNetUserTokens_AspNetUsers_UserId", column: x => x.UserId, principalTable: "AspNetUsers", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( name: "IX_AspNetRoleClaims_RoleId", table: "AspNetRoleClaims", column: "RoleId"); migrationBuilder.CreateIndex( name: "RoleNameIndex", table: "AspNetRoles", column: "NormalizedName", unique: true, filter: "[NormalizedName] IS NOT NULL"); migrationBuilder.CreateIndex( name: "IX_AspNetUserClaims_UserId", table: "AspNetUserClaims", column: "UserId"); migrationBuilder.CreateIndex( name: "IX_AspNetUserLogins_UserId", table: "AspNetUserLogins", column: "UserId"); migrationBuilder.CreateIndex( name: "IX_AspNetUserRoles_RoleId", table: "AspNetUserRoles", column: "RoleId"); migrationBuilder.CreateIndex( name: "EmailIndex", table: "AspNetUsers", column: "NormalizedEmail"); migrationBuilder.CreateIndex( name: "UserNameIndex", table: "AspNetUsers", column: "NormalizedUserName", unique: true, filter: "[NormalizedUserName] IS NOT NULL"); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "AspNetRoleClaims"); migrationBuilder.DropTable( name: "AspNetUserClaims"); migrationBuilder.DropTable( name: "AspNetUserLogins"); migrationBuilder.DropTable( name: "AspNetUserRoles"); migrationBuilder.DropTable( name: "AspNetUserTokens"); migrationBuilder.DropTable( name: "AspNetRoles"); migrationBuilder.DropTable( name: "AspNetUsers"); } } ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/Migrations/UsersDb/ApplicationDbContextModelSnapshot.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // namespace SqlServer.Migrations.UsersDb; [DbContext(typeof(ApplicationDbContext))] partial class ApplicationDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.0") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.Models.ApplicationUser", b => { b.Property("Id") .HasColumnType("nvarchar(450)"); b.Property("AccessFailedCount") .HasColumnType("int"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnType("nvarchar(max)"); b.Property("Email") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.Property("EmailConfirmed") .HasColumnType("bit"); b.Property("LockoutEnabled") .HasColumnType("bit"); b.Property("LockoutEnd") .HasColumnType("datetimeoffset"); b.Property("NormalizedEmail") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.Property("NormalizedUserName") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.Property("PasswordHash") .HasColumnType("nvarchar(max)"); b.Property("PhoneNumber") .HasColumnType("nvarchar(max)"); b.Property("PhoneNumberConfirmed") .HasColumnType("bit"); b.Property("SecurityStamp") .HasColumnType("nvarchar(max)"); b.Property("TwoFactorEnabled") .HasColumnType("bit"); b.Property("UserName") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.HasKey("Id"); b.HasIndex("NormalizedEmail") .HasName("EmailIndex"); b.HasIndex("NormalizedUserName") .IsUnique() .HasName("UserNameIndex") .HasFilter("[NormalizedUserName] IS NOT NULL"); b.ToTable("AspNetUsers"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRole", b => { b.Property("Id") .HasColumnType("nvarchar(450)"); b.Property("ConcurrencyStamp") .IsConcurrencyToken() .HasColumnType("nvarchar(max)"); b.Property("Name") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.Property("NormalizedName") .HasColumnType("nvarchar(256)") .HasMaxLength(256); b.HasKey("Id"); b.HasIndex("NormalizedName") .IsUnique() .HasName("RoleNameIndex") .HasFilter("[NormalizedName] IS NOT NULL"); b.ToTable("AspNetRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); b.Property("ClaimValue") .HasColumnType("nvarchar(max)"); b.Property("RoleId") .IsRequired() .HasColumnType("nvarchar(450)"); b.HasKey("Id"); b.HasIndex("RoleId"); b.ToTable("AspNetRoleClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClaimType") .HasColumnType("nvarchar(max)"); b.Property("ClaimValue") .HasColumnType("nvarchar(max)"); b.Property("UserId") .IsRequired() .HasColumnType("nvarchar(450)"); b.HasKey("Id"); b.HasIndex("UserId"); b.ToTable("AspNetUserClaims"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.Property("LoginProvider") .HasColumnType("nvarchar(450)"); b.Property("ProviderKey") .HasColumnType("nvarchar(450)"); b.Property("ProviderDisplayName") .HasColumnType("nvarchar(max)"); b.Property("UserId") .IsRequired() .HasColumnType("nvarchar(450)"); b.HasKey("LoginProvider", "ProviderKey"); b.HasIndex("UserId"); b.ToTable("AspNetUserLogins"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.Property("UserId") .HasColumnType("nvarchar(450)"); b.Property("RoleId") .HasColumnType("nvarchar(450)"); b.HasKey("UserId", "RoleId"); b.HasIndex("RoleId"); b.ToTable("AspNetUserRoles"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.Property("UserId") .HasColumnType("nvarchar(450)"); b.Property("LoginProvider") .HasColumnType("nvarchar(450)"); b.Property("Name") .HasColumnType("nvarchar(450)"); b.Property("Value") .HasColumnType("nvarchar(max)"); b.HasKey("UserId", "LoginProvider", "Name"); b.ToTable("AspNetUserTokens"); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityRoleClaim", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserClaim", b => { b.HasOne("IdentityServer8.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserLogin", b => { b.HasOne("IdentityServer8.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserRole", b => { b.HasOne("Microsoft.AspNetCore.Identity.IdentityRole", null) .WithMany() .HasForeignKey("RoleId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); b.HasOne("IdentityServer8.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("Microsoft.AspNetCore.Identity.IdentityUserToken", b => { b.HasOne("IdentityServer8.Models.ApplicationUser", null) .WithMany() .HasForeignKey("UserId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); #pragma warning restore 612, 618 } } ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/Migrations/UsersDb.sql ================================================ IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL BEGIN CREATE TABLE [__EFMigrationsHistory] ( [MigrationId] nvarchar(150) NOT NULL, [ProductVersion] nvarchar(32) NOT NULL, CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId]) ); END; GO CREATE TABLE [AspNetRoles] ( [Id] nvarchar(450) NOT NULL, [Name] nvarchar(256) NULL, [NormalizedName] nvarchar(256) NULL, [ConcurrencyStamp] nvarchar(max) NULL, CONSTRAINT [PK_AspNetRoles] PRIMARY KEY ([Id]) ); GO CREATE TABLE [AspNetUsers] ( [Id] nvarchar(450) NOT NULL, [UserName] nvarchar(256) NULL, [NormalizedUserName] nvarchar(256) NULL, [Email] nvarchar(256) NULL, [NormalizedEmail] nvarchar(256) NULL, [EmailConfirmed] bit NOT NULL, [PasswordHash] nvarchar(max) NULL, [SecurityStamp] nvarchar(max) NULL, [ConcurrencyStamp] nvarchar(max) NULL, [PhoneNumber] nvarchar(max) NULL, [PhoneNumberConfirmed] bit NOT NULL, [TwoFactorEnabled] bit NOT NULL, [LockoutEnd] datetimeoffset NULL, [LockoutEnabled] bit NOT NULL, [AccessFailedCount] int NOT NULL, CONSTRAINT [PK_AspNetUsers] PRIMARY KEY ([Id]) ); GO CREATE TABLE [AspNetRoleClaims] ( [Id] int NOT NULL IDENTITY, [RoleId] nvarchar(450) NOT NULL, [ClaimType] nvarchar(max) NULL, [ClaimValue] nvarchar(max) NULL, CONSTRAINT [PK_AspNetRoleClaims] PRIMARY KEY ([Id]), CONSTRAINT [FK_AspNetRoleClaims_AspNetRoles_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [AspNetRoles] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [AspNetUserClaims] ( [Id] int NOT NULL IDENTITY, [UserId] nvarchar(450) NOT NULL, [ClaimType] nvarchar(max) NULL, [ClaimValue] nvarchar(max) NULL, CONSTRAINT [PK_AspNetUserClaims] PRIMARY KEY ([Id]), CONSTRAINT [FK_AspNetUserClaims_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [AspNetUsers] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [AspNetUserLogins] ( [LoginProvider] nvarchar(450) NOT NULL, [ProviderKey] nvarchar(450) NOT NULL, [ProviderDisplayName] nvarchar(max) NULL, [UserId] nvarchar(450) NOT NULL, CONSTRAINT [PK_AspNetUserLogins] PRIMARY KEY ([LoginProvider], [ProviderKey]), CONSTRAINT [FK_AspNetUserLogins_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [AspNetUsers] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [AspNetUserRoles] ( [UserId] nvarchar(450) NOT NULL, [RoleId] nvarchar(450) NOT NULL, CONSTRAINT [PK_AspNetUserRoles] PRIMARY KEY ([UserId], [RoleId]), CONSTRAINT [FK_AspNetUserRoles_AspNetRoles_RoleId] FOREIGN KEY ([RoleId]) REFERENCES [AspNetRoles] ([Id]) ON DELETE CASCADE, CONSTRAINT [FK_AspNetUserRoles_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [AspNetUsers] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [AspNetUserTokens] ( [UserId] nvarchar(450) NOT NULL, [LoginProvider] nvarchar(450) NOT NULL, [Name] nvarchar(450) NOT NULL, [Value] nvarchar(max) NULL, CONSTRAINT [PK_AspNetUserTokens] PRIMARY KEY ([UserId], [LoginProvider], [Name]), CONSTRAINT [FK_AspNetUserTokens_AspNetUsers_UserId] FOREIGN KEY ([UserId]) REFERENCES [AspNetUsers] ([Id]) ON DELETE CASCADE ); GO CREATE INDEX [IX_AspNetRoleClaims_RoleId] ON [AspNetRoleClaims] ([RoleId]); GO CREATE UNIQUE INDEX [RoleNameIndex] ON [AspNetRoles] ([NormalizedName]) WHERE [NormalizedName] IS NOT NULL; GO CREATE INDEX [IX_AspNetUserClaims_UserId] ON [AspNetUserClaims] ([UserId]); GO CREATE INDEX [IX_AspNetUserLogins_UserId] ON [AspNetUserLogins] ([UserId]); GO CREATE INDEX [IX_AspNetUserRoles_RoleId] ON [AspNetUserRoles] ([RoleId]); GO CREATE INDEX [EmailIndex] ON [AspNetUsers] ([NormalizedEmail]); GO CREATE UNIQUE INDEX [UserNameIndex] ON [AspNetUsers] ([NormalizedUserName]) WHERE [NormalizedUserName] IS NOT NULL; GO INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) VALUES (N'20200323135751_Users', N'3.1.0'); GO ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace SqlServer; class Program { public static void Main(string[] args) { var host = BuildWebHost(args); SeedData.EnsureSeedData(host.Services); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() .Build(); } ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:7603/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "SqlServer": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:7604/" } } } ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/SeedData.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost; public class SeedData { public static void EnsureSeedData(IServiceProvider serviceProvider) { using (var scope = serviceProvider.GetRequiredService().CreateScope()) { var context = scope.ServiceProvider.GetService(); context.Database.Migrate(); var userMgr = scope.ServiceProvider.GetRequiredService>(); var alice = userMgr.FindByNameAsync("alice").Result; if (alice == null) { alice = new ApplicationUser { UserName = "alice" }; var result = userMgr.CreateAsync(alice, "Pass123$").Result; if (!result.Succeeded) { throw new Exception(result.Errors.First().Description); } result = userMgr.AddClaimsAsync(alice, new Claim[]{ new Claim(JwtClaimTypes.Name, "Alice Smith"), new Claim(JwtClaimTypes.GivenName, "Alice"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://alice.com"), new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer8.IdentityServerConstants.ClaimValueTypes.Json) }).Result; if (!result.Succeeded) { throw new Exception(result.Errors.First().Description); } Console.WriteLine("alice created"); } else { Console.WriteLine("alice already exists"); } var bob = userMgr.FindByNameAsync("bob").Result; if (bob == null) { bob = new ApplicationUser { UserName = "bob" }; var result = userMgr.CreateAsync(bob, "Pass123$").Result; if (!result.Succeeded) { throw new Exception(result.Errors.First().Description); } result = userMgr.AddClaimsAsync(bob, new Claim[]{ new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServer8.IdentityServerConstants.ClaimValueTypes.Json), new Claim("location", "somewhere") }).Result; if (!result.Succeeded) { throw new Exception(result.Errors.First().Description); } Console.WriteLine("bob created"); } else { Console.WriteLine("bob already exists"); } } } } ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/SqlServer.csproj ================================================ ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace SqlServer; public class Startup { public IConfiguration Configuration { get; } public Startup(IConfiguration config) { Configuration = config; } public void ConfigureServices(IServiceCollection services) { var cn = Configuration.GetConnectionString("db"); services.AddDbContext(options => { options.UseSqlServer(cn, dbOpts => dbOpts.MigrationsAssembly(typeof(Startup).Assembly.FullName)); }); services.AddIdentity() .AddEntityFrameworkStores() .AddDefaultTokenProviders(); } public void Configure(IApplicationBuilder app) { } } ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/appsettings.json ================================================ { "ConnectionStrings": { "db": "server=(localdb)\\mssqllocaldb;database=IdentityServer8.AspNetIdentity-8.0.0;trusted_connection=yes;" } } ================================================ FILE: src/AspNetIdentity/migrations/SqlServer/builddb.bat ================================================ rmdir /S /Q Migrations dotnet ef database drop dotnet ef migrations add Users -o Migrations/UsersDb dotnet ef migrations script -o Migrations/UsersDb.sql dotnet ef database update ================================================ FILE: src/AspNetIdentity/src/Decorator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.AspNetIdentity; internal class Decorator { public TService Instance { get; set; } public Decorator(TService instance) { Instance = instance; } } internal class Decorator : Decorator where TImpl : class, TService { public Decorator(TImpl instance) : base(instance) { } } internal class DisposableDecorator : Decorator, IDisposable { public DisposableDecorator(TService instance) : base(instance) { } public void Dispose() { (Instance as IDisposable)?.Dispose(); } } ================================================ FILE: src/AspNetIdentity/src/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.AspNetIdentity; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Validation; global using Microsoft.AspNetCore.Authentication.Cookies; global using Microsoft.AspNetCore.Identity; global using Microsoft.Extensions.Logging; global using System.Security.Claims; global using static IdentityModel.OidcConstants; ================================================ FILE: src/AspNetIdentity/src/IdentityServer8.AspNetIdentity.csproj ================================================ ASP.NET Core Identity Integration for IdentityServer8 true true true True True ================================================ FILE: src/AspNetIdentity/src/IdentityServerBuilderExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// Extension methods to add ASP.NET Identity support to IdentityServer. /// public static class IdentityServerBuilderExtensions { /// /// Configures IdentityServer to use the ASP.NET Identity implementations /// of IUserClaimsPrincipalFactory, IResourceOwnerPasswordValidator, and IProfileService. /// Also configures some of ASP.NET Identity's options for use with IdentityServer (such as claim types to use /// and authentication cookie settings). /// /// The type of the user. /// The builder. /// public static IIdentityServerBuilder AddAspNetIdentity(this IIdentityServerBuilder builder) where TUser : class { builder.Services.AddTransientDecorator, UserClaimsFactory>(); builder.Services.Configure(options => { options.ClaimsIdentity.UserIdClaimType = JwtClaimTypes.Subject; options.ClaimsIdentity.UserNameClaimType = JwtClaimTypes.Name; options.ClaimsIdentity.RoleClaimType = JwtClaimTypes.Role; }); builder.Services.Configure(opts => { opts.OnRefreshingPrincipal = SecurityStampValidatorCallback.UpdatePrincipal; }); builder.Services.ConfigureApplicationCookie(options => { options.Cookie.IsEssential = true; // we need to disable to allow iframe for authorize requests options.Cookie.SameSite = AspNetCore.Http.SameSiteMode.None; }); builder.Services.ConfigureExternalCookie(options => { options.Cookie.IsEssential = true; // https://github.com/alexhiggins732/IdentityServer8/issues/2595 options.Cookie.SameSite = AspNetCore.Http.SameSiteMode.None; }); builder.Services.Configure(IdentityConstants.TwoFactorRememberMeScheme, options => { options.Cookie.IsEssential = true; }); builder.Services.Configure(IdentityConstants.TwoFactorUserIdScheme, options => { options.Cookie.IsEssential = true; }); builder.Services.AddAuthentication(options => { if (options.DefaultAuthenticateScheme == null && options.DefaultScheme == IdentityServerConstants.DefaultCookieAuthenticationScheme) { options.DefaultScheme = IdentityConstants.ApplicationScheme; } }); builder.AddResourceOwnerValidator>(); builder.AddProfileService>(); return builder; } internal static void AddTransientDecorator(this IServiceCollection services) where TService : class where TImplementation : class, TService { services.AddDecorator(); services.AddTransient(); } internal static void AddDecorator(this IServiceCollection services) { var registration = services.LastOrDefault(x => x.ServiceType == typeof(TService)); if (registration == null) { throw new InvalidOperationException("Service type: " + typeof(TService).Name + " not registered."); } if (services.Any(x => x.ServiceType == typeof(Decorator))) { throw new InvalidOperationException("Decorator already registered for type: " + typeof(TService).Name + "."); } services.Remove(registration); if (registration.ImplementationInstance != null) { var type = registration.ImplementationInstance.GetType(); var innerType = typeof(Decorator<,>).MakeGenericType(typeof(TService), type); services.Add(new ServiceDescriptor(typeof(Decorator), innerType, ServiceLifetime.Transient)); services.Add(new ServiceDescriptor(type, registration.ImplementationInstance)); } else if (registration.ImplementationFactory != null) { services.Add(new ServiceDescriptor(typeof(Decorator), provider => { return new DisposableDecorator((TService)registration.ImplementationFactory(provider)); }, registration.Lifetime)); } else { var type = registration.ImplementationType; var innerType = typeof(Decorator<,>).MakeGenericType(typeof(TService), registration.ImplementationType); services.Add(new ServiceDescriptor(typeof(Decorator), innerType, ServiceLifetime.Transient)); services.Add(new ServiceDescriptor(type, type, registration.Lifetime)); } } } ================================================ FILE: src/AspNetIdentity/src/ProfileService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.AspNetIdentity; /// /// IProfileService to integrate with ASP.NET Identity. /// /// The type of the user. /// public class ProfileService : IProfileService where TUser : class { /// /// The claims factory. /// protected readonly IUserClaimsPrincipalFactory ClaimsFactory; /// /// The logger /// protected readonly ILogger> Logger; /// /// The user manager. /// protected readonly UserManager UserManager; /// /// Initializes a new instance of the class. /// /// The user manager. /// The claims factory. public ProfileService(UserManager userManager, IUserClaimsPrincipalFactory claimsFactory) { UserManager = userManager; ClaimsFactory = claimsFactory; } /// /// Initializes a new instance of the class. /// /// The user manager. /// The claims factory. /// The logger. public ProfileService(UserManager userManager, IUserClaimsPrincipalFactory claimsFactory, ILogger> logger) { UserManager = userManager; ClaimsFactory = claimsFactory; Logger = logger; } /// /// This method is called whenever claims about the user are requested (e.g. during token creation or via the userinfo endpoint) /// /// The context. /// public virtual async Task GetProfileDataAsync(ProfileDataRequestContext context) { var sub = context.Subject?.GetSubjectId(); if (sub == null) throw new Exception("No sub claim present"); await GetProfileDataAsync(context, sub); } /// /// Called to get the claims for the subject based on the profile request. /// /// /// /// protected virtual async Task GetProfileDataAsync(ProfileDataRequestContext context, string subjectId) { var user = await FindUserAsync(subjectId); if (user != null) { await GetProfileDataAsync(context, user); } } /// /// Called to get the claims for the user based on the profile request. /// /// /// /// protected virtual async Task GetProfileDataAsync(ProfileDataRequestContext context, TUser user) { var principal = await GetUserClaimsAsync(user); context.AddRequestedClaims(principal.Claims); } /// /// Gets the claims for a user. /// /// /// protected virtual async Task GetUserClaimsAsync(TUser user) { var principal = await ClaimsFactory.CreateAsync(user); if (principal == null) throw new Exception("ClaimsFactory failed to create a principal"); return principal; } /// /// This method gets called whenever identity server needs to determine if the user is valid or active (e.g. if the user's account has been deactivated since they logged in). /// (e.g. during token issuance or validation). /// /// The context. /// public virtual async Task IsActiveAsync(IsActiveContext context) { var sub = context.Subject?.GetSubjectId(); if (sub == null) throw new Exception("No subject Id claim present"); await IsActiveAsync(context, sub); } /// /// Determines if the subject is active. /// /// /// /// protected virtual async Task IsActiveAsync(IsActiveContext context, string subjectId) { var user = await FindUserAsync(subjectId); if (user != null) { await IsActiveAsync(context, user); } else { context.IsActive = false; } } /// /// Determines if the user is active. /// /// /// /// protected virtual async Task IsActiveAsync(IsActiveContext context, TUser user) { context.IsActive = await IsUserActiveAsync(user); } /// /// Returns if the user is active. /// /// /// public virtual Task IsUserActiveAsync(TUser user) { return Task.FromResult(true); } /// /// Loads the user by the subject id. /// /// /// protected virtual async Task FindUserAsync(string subjectId) { var user = await UserManager.FindByIdAsync(subjectId); if (user == null) { Logger?.LogWarning("No user found matching subject Id: {subjectId}", subjectId); } return user; } } ================================================ FILE: src/AspNetIdentity/src/ResourceOwnerPasswordValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.AspNetIdentity; /// /// IResourceOwnerPasswordValidator that integrates with ASP.NET Identity. /// /// The type of the user. /// public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator where TUser : class { private readonly SignInManager _signInManager; private readonly UserManager _userManager; private readonly ILogger> _logger; /// /// Initializes a new instance of the class. /// /// The user manager. /// The sign in manager. /// The logger. public ResourceOwnerPasswordValidator( UserManager userManager, SignInManager signInManager, ILogger> logger) { _userManager = userManager; _signInManager = signInManager; _logger = logger; } /// /// Validates the resource owner password credential /// /// The context. /// public virtual async Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { var user = await _userManager.FindByNameAsync(context.UserName); if (user != null) { var result = await _signInManager.CheckPasswordSignInAsync(user, context.Password, true); if (result.Succeeded) { var sub = await _userManager.GetUserIdAsync(user); _logger.LogInformation("Credentials validated for username: {username}", context.UserName); context.Result = new GrantValidationResult(sub, AuthenticationMethods.Password); return; } else if (result.IsLockedOut) { _logger.LogInformation("Authentication failed for username: {username}, reason: locked out", context.UserName); } else if (result.IsNotAllowed) { _logger.LogInformation("Authentication failed for username: {username}, reason: not allowed", context.UserName); } else { _logger.LogInformation("Authentication failed for username: {username}, reason: invalid credentials", context.UserName); } } else { _logger.LogInformation("No user found matching username: {username}", context.UserName); } context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant); } } ================================================ FILE: src/AspNetIdentity/src/SecurityStampValidatorCallback.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.AspNetIdentity; /// /// Implements callback for SecurityStampValidator's OnRefreshingPrincipal event. /// public class SecurityStampValidatorCallback { /// /// Maintains the claims captured at login time that are not being created by ASP.NET Identity. /// This is needed to preserve claims such as idp, auth_time, amr. /// /// The context. /// public static Task UpdatePrincipal(SecurityStampRefreshingPrincipalContext context) { var newClaimTypes = context.NewPrincipal.Claims.Select(x => x.Type).ToArray(); var currentClaimsToKeep = context.CurrentPrincipal.Claims.Where(x => !newClaimTypes.Contains(x.Type)).ToArray(); var id = context.NewPrincipal.Identities.First(); id.AddClaims(currentClaimsToKeep); return Task.CompletedTask; } } ================================================ FILE: src/AspNetIdentity/src/UserClaimsFactory.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.AspNetIdentity; internal class UserClaimsFactory : IUserClaimsPrincipalFactory where TUser : class { private readonly Decorator> _inner; private UserManager _userManager; public UserClaimsFactory(Decorator> inner, UserManager userManager) { _inner = inner; _userManager = userManager; } public async Task CreateAsync(TUser user) { var principal = await _inner.Instance.CreateAsync(user); var identity = principal.Identities.First(); if (!identity.HasClaim(x => x.Type == JwtClaimTypes.Subject)) { var sub = await _userManager.GetUserIdAsync(user); identity.AddClaim(new Claim(JwtClaimTypes.Subject, sub)); } var username = await _userManager.GetUserNameAsync(user); var usernameClaim = identity.FindFirst(claim => claim.Type == _userManager.Options.ClaimsIdentity.UserNameClaimType && claim.Value == username); if (usernameClaim != null) { identity.RemoveClaim(usernameClaim); identity.AddClaim(new Claim(JwtClaimTypes.PreferredUserName, username)); } if (!identity.HasClaim(x=>x.Type == JwtClaimTypes.Name)) { identity.AddClaim(new Claim(JwtClaimTypes.Name, username)); } if (_userManager.SupportsUserEmail) { var email = await _userManager.GetEmailAsync(user); if (!String.IsNullOrWhiteSpace(email)) { identity.AddClaims(new[] { new Claim(JwtClaimTypes.Email, email), new Claim(JwtClaimTypes.EmailVerified, await _userManager.IsEmailConfirmedAsync(user) ? "true" : "false", ClaimValueTypes.Boolean) }); } } if (_userManager.SupportsUserPhoneNumber) { var phoneNumber = await _userManager.GetPhoneNumberAsync(user); if (!String.IsNullOrWhiteSpace(phoneNumber)) { identity.AddClaims(new[] { new Claim(JwtClaimTypes.PhoneNumber, phoneNumber), new Claim(JwtClaimTypes.PhoneNumberVerified, await _userManager.IsPhoneNumberConfirmedAsync(user) ? "true" : "false", ClaimValueTypes.Boolean) }); } } return principal; } } ================================================ FILE: src/Directory.Build.props ================================================ ================================================ FILE: src/Directory.BuildOld.targets ================================================ 3.1.0 3.1.0 3.1.0 4.1.2-* $(MinVerMajor).$(MinVerMinor).$(MinVerPatch).0 ================================================ FILE: src/EntityFramework/Directory.Build.props ================================================ ================================================ FILE: src/EntityFramework/IdentityServer8.EntityFramework.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.29230.61 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{8BFDA83B-FD73-4776-9C2C-5F7FFE440C1F}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework", "src\IdentityServer8.EntityFramework.csproj", "{AB83F911-8B78-4973-A3A9-2A2D85581F25}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "host\Host.csproj", "{9E9EE58E-4B98-4495-8E8B-00281B3EABDA}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "seeding", "seeding", "{26921EED-9592-4174-94D6-8A0F604029F1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlServer", "migrations\SqlServer\SqlServer.csproj", "{D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{19FEB622-0912-4F5E-840F-827B4B9EF026}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "IdentityServer8.EntityFramework.Tests", "test\IdentityServer8.EntityFramework.Tests\IdentityServer8.EntityFramework.Tests.csproj", "{E3685B06-F135-4318-B841-889C35479D5C}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {AB83F911-8B78-4973-A3A9-2A2D85581F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AB83F911-8B78-4973-A3A9-2A2D85581F25}.Debug|Any CPU.Build.0 = Debug|Any CPU {AB83F911-8B78-4973-A3A9-2A2D85581F25}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB83F911-8B78-4973-A3A9-2A2D85581F25}.Release|Any CPU.Build.0 = Release|Any CPU {9E9EE58E-4B98-4495-8E8B-00281B3EABDA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {9E9EE58E-4B98-4495-8E8B-00281B3EABDA}.Debug|Any CPU.Build.0 = Debug|Any CPU {9E9EE58E-4B98-4495-8E8B-00281B3EABDA}.Release|Any CPU.ActiveCfg = Release|Any CPU {9E9EE58E-4B98-4495-8E8B-00281B3EABDA}.Release|Any CPU.Build.0 = Release|Any CPU {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}.Debug|Any CPU.Build.0 = Debug|Any CPU {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}.Release|Any CPU.Build.0 = Release|Any CPU {E3685B06-F135-4318-B841-889C35479D5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E3685B06-F135-4318-B841-889C35479D5C}.Debug|Any CPU.Build.0 = Debug|Any CPU {E3685B06-F135-4318-B841-889C35479D5C}.Release|Any CPU.ActiveCfg = Release|Any CPU {E3685B06-F135-4318-B841-889C35479D5C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {AB83F911-8B78-4973-A3A9-2A2D85581F25} = {8BFDA83B-FD73-4776-9C2C-5F7FFE440C1F} {9E9EE58E-4B98-4495-8E8B-00281B3EABDA} = {8BFDA83B-FD73-4776-9C2C-5F7FFE440C1F} {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69} = {26921EED-9592-4174-94D6-8A0F604029F1} {E3685B06-F135-4318-B841-889C35479D5C} = {19FEB622-0912-4F5E-840F-827B4B9EF026} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {A023D63E-FDD1-4984-9746-45981BCFBACA} EndGlobalSection EndGlobal ================================================ FILE: src/EntityFramework/README.md ================================================ # IdentityServer8.EntityFramework IdentityServer8.EntityFramework is a package that provides the configuration APIs to add persistence for IdentityServer 4 configuration data that uses EntityFramework as its database abstraction. ## Issues For issues, use the [consolidated IdentityServer8 issue tracker](https://github.com/alexhiggins732/IdentityServer8/issues). ================================================ FILE: src/EntityFramework/build.cmd ================================================ @echo off dotnet run --project build -- %* ================================================ FILE: src/EntityFramework/build.ps1 ================================================ $ErrorActionPreference = "Stop"; dotnet run --project build -- $args ================================================ FILE: src/EntityFramework/build.sh ================================================ #!/usr/bin/env bash set -euo pipefail dotnet run --project build -- "$@" ================================================ FILE: src/EntityFramework/dropdb.bat ================================================ cd src\Host dotnet ef database drop -c PersistedGrantDbContext dotnet ef database drop -c ConfigurationDbContext cd ..\.. ================================================ FILE: src/EntityFramework/host/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using IdentityServerHost.Quickstart.UI; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.Extensions.Options; global using Newtonsoft.Json; global using Serilog; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System.ComponentModel.DataAnnotations; global using System.Diagnostics; global using System.Security.Claims; global using System.Text; global using ILogger = Microsoft.Extensions.Logging.ILogger; global using ClaimValueTypes = System.Security.Claims.ClaimValueTypes; ================================================ FILE: src/EntityFramework/host/Host.csproj ================================================ ================================================ FILE: src/EntityFramework/host/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost; public class Program { public static int Main(string[] args) { Console.Title = "IdentityServer8.EntityFramework"; Activity.DefaultIdFormat = ActivityIdFormat.W3C; Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() //.WriteTo.File(@"IdentityServer8_log.txt") // uncomment to write to Azure diagnostics stream //.WriteTo.File( // @"D:\home\LogFiles\Application\identityserver.txt", // fileSizeLimitBytes: 1_000_000, // rollOnFileSizeLimit: true, // shared: true, // flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) .CreateLogger(); try { Log.Information("Starting host..."); CreateHostBuilder(args).Build().Run(); return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSerilog() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } ================================================ FILE: src/EntityFramework/host/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": true, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:5000", "sslPort": 44334 } }, "profiles": { "Host": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001" } } } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/AccountController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval /// [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController( IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; } /// /// Entry point into the login workflow /// [HttpGet] public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); if (vm.IsExternalLoginOnly) { // we only have one option for logging in and it's an external provider return returnUrl.IsAllowedRedirect() ? RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl = returnUrl.SanitizeForRedirect() }) : Forbid(); } return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (ModelState.IsValid) { // validate username/password against in-memory store if (_users.ValidateCredentials(model.Username, model.Password)) { var user = _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.Client.ClientId)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username }; await HttpContext.SignInAsync(isuser, props); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task LoginCancel(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else { // since we don't have a valid context, then we just go back to the home page return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } } /// /// Show logout page /// [HttpGet] public async Task Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); } /// /// Handle logout page postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await HttpContext.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); } [HttpGet] public IActionResult AccessDenied() { return View(); } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) { var local = context.IdP == IdentityServer8.IdentityServerConstants.LocalIdentityProvider; // this is meant to short circuit the UI and only trigger the one external IdP var vm = new LoginViewModel { EnableLocalLogin = local, ReturnUrl = returnUrl, Username = context?.LoginHint, }; if (!local) { vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; } return vm; } var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes .Where(x => x.DisplayName != null) .Select(x => new ExternalProvider { DisplayName = x.DisplayName ?? x.Name, AuthenticationScheme = x.Name }).ToList(); var allowLocal = true; if (context?.Client.ClientId != null) { var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); if (client != null) { allowLocal = client.EnableLocalLogin; if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) { providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); } } } return new LoginViewModel { AllowRememberLogin = AccountOptions.AllowRememberLogin, EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, ReturnUrl = returnUrl, Username = context?.LoginHint, ExternalProviders = providers.ToArray() }; } private async Task BuildLoginViewModelAsync(LoginInputModel model) { var vm = await BuildLoginViewModelAsync(model.ReturnUrl); vm.Username = model.Username; vm.RememberLogin = model.RememberLogin; return vm; } private async Task BuildLogoutViewModelAsync(string logoutId) { var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; if (User?.Identity.IsAuthenticated != true) { // if the user is not authenticated, then just show logged out page vm.ShowLogoutPrompt = false; return vm; } var context = await _interaction.GetLogoutContextAsync(logoutId); if (context?.ShowSignoutPrompt == false) { // it's safe to automatically sign-out vm.ShowLogoutPrompt = false; return vm; } // show the logout prompt. this prevents attacks where the user // is automatically signed out by another malicious web page. return vm; } private async Task BuildLoggedOutViewModelAsync(string logoutId) { // get context information (client name, post logout redirect URI and iframe for federated signout) var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LoggedOutViewModel { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; if (User?.Identity.IsAuthenticated == true) { var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; if (idp != null && idp != IdentityServer8.IdentityServerConstants.LocalIdentityProvider) { var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); if (providerSupportsSignout) { if (vm.LogoutId == null) { // if there's no current logout context, we need to create one // this captures necessary info from the current logged in user // before we signout and redirect away to the external IdP for signout vm.LogoutId = await _interaction.CreateLogoutContextAsync(); } vm.ExternalAuthenticationScheme = idp; } } } return vm; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/AccountOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/ExternalController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly ILogger _logger; private readonly IEventService _events; public ExternalController( IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService events, ILogger logger, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _logger = logger; _events = events; } /// /// initiate roundtrip to external authentication provider /// [HttpGet] public IActionResult Challenge(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; // validate returnUrl - either it is a valid OIDC URL or back to a local page if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } // start challenge and roundtrip the return URL and scheme var props = new AuthenticationProperties { RedirectUri = Url.Action(nameof(Callback)), Items = { { "returnUrl", returnUrl }, { "scheme", scheme }, } }; return Challenge(props, scheme); } /// /// Post processing of external authentication /// [HttpGet] public async Task Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = AutoProvisionUser(provider, providerUserId, claims); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); return (user, provider, providerUserId, claims); } private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) { var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); return user; } // if the external login is OIDC-based, there are certain things we need to preserve to make logout work // this will be different for WS-Fed, SAML2p or other protocols private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) { // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout var idToken = externalResult.Properties.GetTokenValue("id_token"); if (idToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); } } } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/ExternalProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ExternalProvider { public string DisplayName { get; set; } public string AuthenticationScheme { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/LoggedOutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoggedOutViewModel { public string PostLogoutRedirectUri { get; set; } public string ClientName { get; set; } public string SignOutIframeUrl { get; set; } public bool AutomaticRedirectAfterSignOut { get; set; } public string LogoutId { get; set; } public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; public string ExternalAuthenticationScheme { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/LoginInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginInputModel { [Required] public string Username { get; set; } [Required] public string Password { get; set; } public bool RememberLogin { get; set; } public string ReturnUrl { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginViewModel : LoginInputModel { public bool AllowRememberLogin { get; set; } = true; public bool EnableLocalLogin { get; set; } = true; public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/LogoutInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutInputModel { public string LogoutId { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/LogoutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutViewModel : LogoutInputModel { public bool ShowLogoutPrompt { get; set; } = true; } ================================================ FILE: src/EntityFramework/host/Quickstart/Account/RedirectViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class RedirectViewModel { public string RedirectUrl { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Consent/ConsentController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This controller processes the consent UI /// [SecurityHeaders] [Authorize] public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IEventService _events; private readonly ILogger _logger; public ConsentController( IIdentityServerInteractionService interaction, IEventService events, ILogger logger) { _interaction = interaction; _events = events; _logger = logger; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { var result = await ProcessConsent(model); if (result.IsRedirect) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context?.IsNativeClient() == true) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", result.RedirectUri); } return result.RedirectUri.IsAllowedRedirect() ? Redirect(result.RedirectUri.SanitizeForRedirect()) : Forbid(); } if (result.HasValidationError) { ModelState.AddModelError(string.Empty, result.ValidationError); } if (result.ShowView) { return View("Index", result.ViewModel); } return View("Error"); } /*****************************************/ /* helper APIs for the ConsentController */ /*****************************************/ private async Task ProcessConsent(ConsentInputModel model) { var result = new ProcessConsentResult(); // validate return url is still valid var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model?.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model?.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); } return result; } private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { return CreateConsentViewModel(model, returnUrl, request); } else { _logger.LogError("No consent request matching request: {0}", returnUrl.SanitizeForLog()); } return null; } private ConsentViewModel CreateConsentViewModel( ConsentInputModel model, string returnUrl, AuthorizationRequest request) { var vm = new ConsentViewModel { RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), Description = model?.Description, ReturnUrl = returnUrl, ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach(var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { var displayName = apiScope.DisplayName ?? apiScope.Name; if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) { displayName += ":" + parsedScopeValue.ParsedParameter; } return new ScopeViewModel { Value = parsedScopeValue.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Consent/ConsentInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentInputModel { public string Button { get; set; } public IEnumerable ScopesConsented { get; set; } public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Consent/ConsentOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentOptions { public static bool EnableOfflineAccess = true; public static string OfflineAccessDisplayName = "Offline Access"; public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; } ================================================ FILE: src/EntityFramework/host/Quickstart/Consent/ConsentViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentViewModel : ConsentInputModel { public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public bool AllowRememberConsent { get; set; } public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Consent/ProcessConsentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ProcessConsentResult { public bool IsRedirect => RedirectUri != null; public string RedirectUri { get; set; } public Client Client { get; set; } public bool ShowView => ViewModel != null; public ConsentViewModel ViewModel { get; set; } public bool HasValidationError => ValidationError != null; public string ValidationError { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Consent/ScopeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ScopeViewModel { public string Value { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Emphasize { get; set; } public bool Required { get; set; } public bool Checked { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Device/DeviceAuthorizationInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Device/DeviceAuthorizationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationViewModel : ConsentViewModel { public string UserCode { get; set; } public bool ConfirmUserCode { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Device/DeviceController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [Authorize] [SecurityHeaders] public class DeviceController : Controller { private readonly IDeviceFlowInteractionService _interaction; private readonly IEventService _events; private readonly IOptions _options; private readonly ILogger _logger; public DeviceController( IDeviceFlowInteractionService interaction, IEventService eventService, IOptions options, ILogger logger) { _interaction = interaction; _events = eventService; _options = options; _logger = logger; } [HttpGet] public async Task Index() { string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; string userCode = Request.Query[userCodeParamName]; if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); vm.ConfirmUserCode = true; return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task UserCodeCapture(string userCode) { var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task Callback(DeviceAuthorizationInputModel model) { if (model == null) throw new ArgumentNullException(nameof(model)); var result = await ProcessConsent(model); if (result.HasValidationError) return View("Error"); return View("Success"); } private async Task ProcessConsent(DeviceAuthorizationInputModel model) { var result = new ProcessConsentResult(); var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.UserCode, model); } return result; } private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(userCode); if (request != null) { return CreateConsentViewModel(userCode, model, request); } return null; } private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { UserCode = userCode, Description = model?.Description, RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? DisplayName = apiScope.DisplayName ?? apiScope.Name, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Diagnostics/DiagnosticsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [Authorize] public class DiagnosticsController : Controller { public async Task Index() { var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) { return NotFound(); } var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); return View(model); } } ================================================ FILE: src/EntityFramework/host/Quickstart/Diagnostics/DiagnosticsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DiagnosticsViewModel { public DiagnosticsViewModel(AuthenticateResult result) { AuthenticateResult = result; if (result.Properties.Items.ContainsKey("client_list")) { var encoded = result.Properties.Items["client_list"]; var bytes = Base64Url.Decode(encoded); var value = Encoding.UTF8.GetString(bytes); Clients = JsonConvert.DeserializeObject(value); } } public AuthenticateResult AuthenticateResult { get; } public IEnumerable Clients { get; } = new List(); } ================================================ FILE: src/EntityFramework/host/Quickstart/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public static class Extensions { /// /// Checks if the redirect URI is for a native client. /// /// public static bool IsNativeClient(this AuthorizationRequest context) { return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); } public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) { controller.HttpContext.Response.StatusCode = 200; controller.HttpContext.Response.Headers["Location"] = ""; return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); } } ================================================ FILE: src/EntityFramework/host/Quickstart/Grants/GrantsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller allows a user to revoke grants given to clients /// [SecurityHeaders] [Authorize] public class GrantsController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; private readonly IResourceStore _resources; private readonly IEventService _events; public GrantsController(IIdentityServerInteractionService interaction, IClientStore clients, IResourceStore resources, IEventService events) { _interaction = interaction; _clients = clients; _resources = resources; _events = events; } /// /// Show list of grants /// [HttpGet] public async Task Index() { return View("Index", await BuildViewModelAsync()); } /// /// Handle postback to revoke a client /// [HttpPost] [ValidateAntiForgeryToken] public async Task Revoke(string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return RedirectToAction("Index"); } private async Task BuildViewModelAsync() { var grants = await _interaction.GetAllUserGrantsAsync(); var list = new List(); foreach(var grant in grants) { var client = await _clients.FindClientByIdAsync(grant.ClientId); if (client != null) { var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); var item = new GrantViewModel() { ClientId = client.ClientId, ClientName = client.ClientName ?? client.ClientId, ClientLogoUrl = client.LogoUri, ClientUrl = client.ClientUri, Description = grant.Description, Created = grant.CreationTime, Expires = grant.Expiration, IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() }; list.Add(item); } } return new GrantsViewModel { Grants = list }; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Grants/GrantsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class GrantsViewModel { public IEnumerable Grants { get; set; } } public class GrantViewModel { public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public string Description { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Home/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ErrorViewModel { public ErrorViewModel() { } public ErrorViewModel(string error) { Error = new ErrorMessage { Error = error }; } public ErrorMessage Error { get; set; } } ================================================ FILE: src/EntityFramework/host/Quickstart/Home/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) { _interaction = interaction; _environment = environment; _logger = logger; } public IActionResult Index() { if (_environment.IsDevelopment()) { // only show in development return View(); } _logger.LogInformation("Homepage is disabled in production. Returning 404."); return NotFound(); } /// /// Shows the error page /// public async Task Error(string errorId) { var vm = new ErrorViewModel(); // retrieve error details from identityserver var message = await _interaction.GetErrorContextAsync(errorId); if (message != null) { vm.Error = message; if (!_environment.IsDevelopment()) { // only show in development message.ErrorDescription = null; } } return View("Error", vm); } } ================================================ FILE: src/EntityFramework/host/Quickstart/SecurityHeadersAttribute.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class SecurityHeadersAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { var result = context.Result; if (result is ViewResult) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) { context.HttpContext.Response.Headers.Append("X-Content-Type-Options", "nosniff"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) { context.HttpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; // also consider adding upgrade-insecure-requests once you have HTTPS in place for production //csp += "upgrade-insecure-requests;"; // also an example if you need client images to be displayed from twitter // csp += "img-src 'self' https://pbs.twimg.com;"; // once for standards compliant browsers if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("Content-Security-Policy", csp); } // and once again for IE if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("X-Content-Security-Policy", csp); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy var referrer_policy = "no-referrer"; if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) { context.HttpContext.Response.Headers.Append("Referrer-Policy", referrer_policy); } } } } ================================================ FILE: src/EntityFramework/host/Quickstart/TestUsers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using JsonSerializer = System.Text.Json.JsonSerializer; namespace IdentityServerHost.Quickstart.UI; public class TestUsers { public static List Users { get { var address = new { street_address = "One Hacker Way", locality = "Heidelberg", postal_code = 69118, country = "Germany" }; return new List { new TestUser { SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new Claim(JwtClaimTypes.Name, "Alice Smith"), new Claim(JwtClaimTypes.GivenName, "Alice"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://alice.com"), new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json) } }, new TestUser { SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json) } } }; } } } ================================================ FILE: src/EntityFramework/host/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore; namespace IdentityServerHost; public class Startup { private readonly IConfiguration _config; public Startup(IConfiguration config) { _config = config; } public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); var connectionString = _config.GetConnectionString("db"); services.AddIdentityServer() .AddDeveloperSigningCredential() .AddTestUsers(TestUsers.Users) // this adds the config data from DB (clients, resources, CORS) .AddConfigurationStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString); }) // this adds the operational data from DB (codes, tokens, consents) .AddOperationalStore(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString); // this enables automatic token cleanup. this is optional. options.EnableTokenCleanup = true; options.TokenCleanupInterval = 5; // interval in seconds, short for testing }); // this is something you will want in production to reduce load on and requests to the DB //.AddConfigurationStoreCache(); } public void Configure(IApplicationBuilder app) { app.UseDeveloperExceptionPage(); app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } } ================================================ FILE: src/EntityFramework/host/TestOperationalStoreNotification.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework; using IdentityServer8.EntityFramework.Entities; using PersistedGrant = IdentityServer8.EntityFramework.Entities.PersistedGrant; namespace IdentityServerHost { public class TestOperationalStoreNotification : IOperationalStoreNotification { public TestOperationalStoreNotification() { Console.WriteLine("ctor"); } public Task PersistedGrantsRemovedAsync(IEnumerable persistedGrants) { foreach (var grant in persistedGrants) { Console.WriteLine("cleaned: " + grant.Type); } return Task.CompletedTask; } public Task DeviceCodesRemovedAsync(IEnumerable deviceCodes) { foreach (var deviceCode in deviceCodes) { Console.WriteLine("cleaned device code"); } return Task.CompletedTask; } } } ================================================ FILE: src/EntityFramework/host/Views/Account/AccessDenied.cshtml ================================================ 

    Access Denied

    You do not have access to that resource.

    ================================================ FILE: src/EntityFramework/host/Views/Account/LoggedOut.cshtml ================================================ @model LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user ViewData["signed-out"] = true; }

    Logout You are now logged out

    @if (Model.PostLogoutRedirectUri != null) {
    Click here to return to the @Model.ClientName application.
    } @if (Model.SignOutIframeUrl != null) { }
    @section scripts { @if (Model.AutomaticRedirectAfterSignOut) { } } ================================================ FILE: src/EntityFramework/host/Views/Account/Login.cshtml ================================================ @model LoginViewModel ================================================ FILE: src/EntityFramework/host/Views/Account/Logout.cshtml ================================================ @model LogoutViewModel

    Logout

    Would you like to logout of IdentityServer?

    ================================================ FILE: src/EntityFramework/host/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: src/EntityFramework/host/Views/Device/Success.cshtml ================================================

    Success

    You have successfully authorized the device

    ================================================ FILE: src/EntityFramework/host/Views/Device/UserCodeCapture.cshtml ================================================ @model string

    User Code

    Please enter the code displayed on your device.

    ================================================ FILE: src/EntityFramework/host/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
    @if (Model.ClientLogoUrl != null) { }

    @Model.ClientName is requesting your permission

    @if (Model.ConfirmUserCode) {

    Please confirm that the authorization request quotes the code: @Model.UserCode.

    }

    Uncheck the permissions you do not wish to grant.

    @if (Model.IdentityScopes.Any()) {
    Personal Information
      @foreach (var scope in Model.IdentityScopes) { }
    } @if (Model.ApiScopes.Any()) {
    Application Access
      @foreach (var scope in Model.ApiScopes) { }
    }
    Description
    @if (Model.AllowRememberConsent) {
    }
    @if (Model.ClientUrl != null) { @Model.ClientName }
    ================================================ FILE: src/EntityFramework/host/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

    Authentication Cookie

    Claims

    @foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in Model.AuthenticateResult.Properties.Items) {
    @prop.Key
    @prop.Value
    } @if (Model.Clients.Any()) {
    Clients
    @{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
    }
    ================================================ FILE: src/EntityFramework/host/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

    Client Application Permissions

    Below is the list of applications you have given permission to and the resources they have access to.

    @if (Model.Grants.Any() == false) {
    You have not given access to any applications
    } else { foreach (var grant in Model.Grants) {
    @if (grant.ClientLogoUrl != null) { } @grant.ClientName
      @if (grant.Description != null) {
    • @grant.Description
    • }
    • @grant.Created.ToString("yyyy-MM-dd")
    • @if (grant.Expires.HasValue) {
    • @grant.Expires.Value.ToString("yyyy-MM-dd")
    • } @if (grant.IdentityGrantNames.Any()) {
      • @foreach (var name in grant.IdentityGrantNames) {
      • @name
      • }
    • } @if (grant.ApiGrantNames.Any()) {
      • @foreach (var name in grant.ApiGrantNames) {
      • @name
      • }
    • }
    } }
    ================================================ FILE: src/EntityFramework/host/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @using System.Reflection @{ var version = typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.GetCustomAttribute()?.InformationalVersion.Split('+').First(); }

    Welcome to IdentityServer8 (version @version)

    ================================================ FILE: src/EntityFramework/host/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

    Error

    Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
    @errorDescription
    } }
    @if (request_id != null) {
    Request Id: @request_id
    }
    ================================================ FILE: src/EntityFramework/host/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

    You are now being returned to the application

    Once complete, you may close this tab.

    ================================================ FILE: src/EntityFramework/host/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
    @RenderBody()
    @RenderSection("scripts", required: false) ================================================ FILE: src/EntityFramework/host/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: src/EntityFramework/host/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: src/EntityFramework/host/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: src/EntityFramework/host/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: src/EntityFramework/host/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: src/EntityFramework/host/appsettings.json ================================================ { "ConnectionStrings": { "db": "server=(localdb)\\mssqllocaldb;database=IdentityServer8.EntityFramework-4.0.0;trusted_connection=yes;", } } ================================================ FILE: src/EntityFramework/host/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: src/EntityFramework/host/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: src/EntityFramework/host/wwwroot/js/signin-redirect.js ================================================ //window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: src/EntityFramework/host/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: src/EntityFramework/migrations/SqlServer/Configuration/Clients.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using System.Collections.Generic; namespace IdentityServerHost.Configuration; public static class Clients { public static IEnumerable Get() { var clients = new List(); clients.AddRange(ClientsConsole.Get()); clients.AddRange(ClientsWeb.Get()); return clients; } } ================================================ FILE: src/EntityFramework/migrations/SqlServer/Configuration/ClientsConsole.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using IdentityServer8; using IdentityServer8.Models; namespace IdentityServerHost.Configuration; public static class ClientsConsole { public static IEnumerable Get() { return new List { /////////////////////////////////////////////////////////////// // Console-based Client /////////////////////////////////////////////////////////////// /////////////////////////////////////////// // Console Client Credentials Flow Sample ////////////////////////////////////////// new Client { ClientId = "client", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1", IdentityServerConstants.LocalApi.ScopeName} }, /////////////////////////////////////////// // Console Structured Scope Sample ////////////////////////////////////////// new Client { ClientId = "parameterized.client", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "transaction" } }, /////////////////////////////////////////// // X509 mTLS Client ////////////////////////////////////////// new Client { ClientId = "mtls", ClientSecrets = { // new Secret(@"CN=mtls.test, OU=ROO\ballen@roo, O=mkcert development certificate", "mtls.test") // { // Type = SecretTypes.X509CertificateName // }, new Secret("5D9E9B6B333CD42C99D1DE6175CC0F3EF99DDF68", "mtls.test") { Type = IdentityServerConstants.SecretTypes.X509CertificateThumbprint }, }, AccessTokenType = AccessTokenType.Jwt, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console Client Credentials Flow with client JWT assertion ////////////////////////////////////////// new Client { ClientId = "client.jwt", ClientSecrets = { new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = "MIIEgTCCAumgAwIBAgIQDMMu7l/umJhfEbzJMpcttzANBgkqhkiG9w0BAQsFADCBkzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTQwMgYDVQQLDCtkb21pbmlja0Bkb21icDE2LmZyaXR6LmJveCAoRG9taW5pY2sgQmFpZXIpMTswOQYDVQQDDDJta2NlcnQgZG9taW5pY2tAZG9tYnAxNi5mcml0ei5ib3ggKERvbWluaWNrIEJhaWVyKTAeFw0xOTA2MDEwMDAwMDBaFw0zMDAxMDMxMjM0MDdaMHAxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTE0MDIGA1UECwwrZG9taW5pY2tAZG9tYnAxNi5mcml0ei5ib3ggKERvbWluaWNrIEJhaWVyKTEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNtpipaS8k1zA6w0Aoy8U4l+8zM4jHhhblExf3PULrMR6RauxniTki8p+P8CsZT4V8A4qo+JwsgpLIHrVQrbt9DEhHfBKzxwHqt+GoHt7byTfTtp8A/5nLhYc/5CW4HiR194gVx5+HAlvt+BriMTb1czvTf+H20dj41yUPsN7nMdyRLF+uXapQYMLYnq2BJIDq83mqGwojHk7d+N6GwoO95jlyas7KSoj8/FvfbaqkRNx0446hqPOzFHKc8er8K5VrLp6tVjh8ZJyY0F0dKgx6yWITsL54ctbj/cCyfuGjWEMbS2XXgc+x/xQMnmpfhK1qQAUn9jg5EzF9n6mQomOwIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUEMUlw41YsKZQVls3pEG6CrJk4O8wEQYDVR0RBAowCIIGY2xpZW50MA0GCSqGSIb3DQEBCwUAA4IBgQC0TjNY4Q3Wmw7ggamDImV6HUng3WbYGLYbbL2e3myBrjIxGd1Bi8ZyOu8qeUMIRAbZt2YsSX5S8kx0biaVg2zC+aO5eHhEWMwKB66huInXFjI4wtxZ22r+33fg1R0cLuEUePhftOWrbL0MS4YXVyn9HUMWO4WptG9PJdxNw1UbEB8nw3FkVOdAC9RGqiqalSK+E2UT/kUbTIQ1gPSdQ3nh52mre0H/T9+IRqiozJtNK/CQg4NuEV7rUXHnp7Fmigp6RIJ4TCozglspL341y0rV8M7npU1FYZC2UKNr4ed+GOO1n/sF3LbXDlPXwne99CVVn85wjDaevoR7Md0y2KwE9EggLYcViXNehx4YVv/BjfgqxW8NxiKAxP6kPOZE0XdBrZj2rmcDcGOXCzzYpcduKhFyTOpA0K5RNGC3j1KOUjPVlOtLvjASP7udBEYNfH3mgqXAgqNDOEKi2jG9LITv2IyGUsXhTAsKNJ6A6qiDBzDrvPAYDvsfabPq6tRTwjA=" }, new Secret { Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = "{'e':'AQAB','kid':'ZzAjSnraU3bkWGnnAqLapYGpTyNfLbjbzgAPbbW2GEA','kty':'RSA','n':'wWwQFtSzeRjjerpEM5Rmqz_DsNaZ9S1Bw6UbZkDLowuuTCjBWUax0vBMMxdy6XjEEK4Oq9lKMvx9JzjmeJf1knoqSNrox3Ka0rnxXpNAz6sATvme8p9mTXyp0cX4lF4U2J54xa2_S9NF5QWvpXvBeC4GAJx7QaSw4zrUkrc6XyaAiFnLhQEwKJCwUw4NOqIuYvYp_IXhw-5Ti_icDlZS-282PcccnBeOcX7vc21pozibIdmZJKqXNsL1Ibx5Nkx1F1jLnekJAmdaACDjYRLL_6n3W4wUp19UvzB1lGtXcJKLLkqB6YDiZNu16OSiSprfmrRXvYmvD8m6Fnl5aetgKw'}" } }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Custom Grant Sample ////////////////////////////////////////// new Client { ClientId = "client.custom", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = {"custom", "custom.nosubject"}, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console Resource Owner Flow Sample ////////////////////////////////////////// new Client { ClientId = "roclient", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, "custom.profile", "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console Public Resource Owner Flow Sample ////////////////////////////////////////// new Client { ClientId = "roclient.public", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console with PKCE Sample ////////////////////////////////////////// new Client { ClientId = "console.pkce", ClientName = "Console with PKCE Sample", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RedirectUris = {"http://127.0.0.1"}, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // WinConsole with PKCE Sample ////////////////////////////////////////// new Client { ClientId = "winconsole", ClientName = "Windows Console with PKCE Sample", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RedirectUris = {"sample-windows-client://callback"}, RequireConsent = false, AllowOfflineAccess = true, AllowedIdentityTokenSigningAlgorithms = {"ES256"}, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Introspection Client Sample ////////////////////////////////////////// new Client { ClientId = "roclient.reference", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "resource1.scope1", "resource2.scope1" }, AccessTokenType = AccessTokenType.Reference }, /////////////////////////////////////////// // Device Flow Sample ////////////////////////////////////////// new Client { ClientId = "device", ClientName = "Device Flow Client", AllowedGrantTypes = GrantTypes.DeviceFlow, RequireClientSecret = false, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } } }; } } ================================================ FILE: src/EntityFramework/migrations/SqlServer/Configuration/ClientsWeb.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using IdentityServer8; using IdentityServer8.Models; namespace IdentityServerHost.Configuration; public static class ClientsWeb { static string[] allowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1", "transaction" }; public static IEnumerable Get() { return new List { /////////////////////////////////////////// // JS OIDC Sample ////////////////////////////////////////// new Client { ClientId = "js_oidc", ClientName = "JavaScript OIDC Client", ClientUri = "http://identityserver8.io", AllowedGrantTypes = GrantTypes.Code, RequireClientSecret = false, RedirectUris = { "https://localhost:44300/index.html", "https://localhost:44300/callback.html", "https://localhost:44300/silent.html", "https://localhost:44300/popup.html" }, PostLogoutRedirectUris = { "https://localhost:44300/index.html" }, AllowedCorsOrigins = { "https://localhost:44300" }, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Automatic Token Management Sample ////////////////////////////////////////// new Client { ClientId = "mvc.tokenmanagement", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AccessTokenLifetime = 75, RedirectUris = { "https://localhost:44301/signin-oidc" }, FrontChannelLogoutUri = "https://localhost:44301/signout-oidc", PostLogoutRedirectUris = { "https://localhost:44301/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Code Flow Sample ////////////////////////////////////////// new Client { ClientId = "mvc.code", ClientName = "MVC Code Flow", ClientUri = "http://identityserver8.io", ClientSecrets = { new Secret("secret".Sha256()) }, RequireConsent = true, AllowedGrantTypes = GrantTypes.Code, RedirectUris = { "https://localhost:44302/signin-oidc" }, FrontChannelLogoutUri = "https://localhost:44302/signout-oidc", PostLogoutRedirectUris = { "https://localhost:44302/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Hybrid Flow Sample (Back Channel logout) ////////////////////////////////////////// new Client { ClientId = "mvc.hybrid.backchannel", ClientName = "MVC Hybrid (with BackChannel logout)", ClientUri = "http://identityserver8.io", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Hybrid, RequirePkce = false, RedirectUris = { "https://localhost:44303/signin-oidc" }, BackChannelLogoutUri = "https://localhost:44303/logout", PostLogoutRedirectUris = { "https://localhost:44303/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes } }; } } ================================================ FILE: src/EntityFramework/migrations/SqlServer/Configuration/Resources.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityModel; using IdentityServer8.Models; using System.Collections.Generic; using static IdentityServer8.IdentityServerConstants; namespace IdentityServerHost.Configuration; public class Resources { // identity resources represent identity data about a user that can be requested via the scope parameter (OpenID Connect) public static readonly IEnumerable IdentityResources = new[] { // some standard scopes from the OIDC spec new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email(), // custom identity resource with some consolidated claims new IdentityResource("custom.profile", new[] { JwtClaimTypes.Name, JwtClaimTypes.Email, "location" }) }; // API scopes represent values that describe scope of access and can be requested by the scope parameter (OAuth) public static readonly IEnumerable ApiScopes = new[] { // local API scope new ApiScope(LocalApi.ScopeName), // resource specific scopes new ApiScope("resource1.scope1"), new ApiScope("resource2.scope1"), // a scope without resource association new ApiScope("scope3"), // a scope shared by multiple resources new ApiScope("shared.scope"), // a parameterized scope new ApiScope("transaction", "Transaction") { Description = "Some Transaction" } }; // API resources are more formal representation of a resource with processing rules and their scopes (if any) public static readonly IEnumerable ApiResources = new[] { new ApiResource("resource1", "Resource 1") { ApiSecrets = { new Secret("secret".Sha256()) }, Scopes = { "resource1.scope1", "shared.scope" } }, // expanded version if more control is needed new ApiResource("resource2", "Resource 2") { ApiSecrets = { new Secret("secret".Sha256()) }, // additional claims to put into access token UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Email }, Scopes = { "resource2.scope1", "shared.scope" } } }; } ================================================ FILE: src/EntityFramework/migrations/SqlServer/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore; namespace SqlServer; class Program { public static void Main(string[] args) { var host = BuildWebHost(args); SeedData.EnsureSeedData(host.Services); } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() .Build(); } ================================================ FILE: src/EntityFramework/migrations/SqlServer/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:7603/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "SqlServer": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:7604/" } } } ================================================ FILE: src/EntityFramework/migrations/SqlServer/SeedData.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.DbContexts; using IdentityServer8.EntityFramework.Mappers; using IdentityServerHost.Configuration; namespace SqlServer; public class SeedData { public static void EnsureSeedData(IServiceProvider serviceProvider) { using (var scope = serviceProvider.GetRequiredService().CreateScope()) { using (var context = scope.ServiceProvider.GetService()) { EnsureSeedData(context); } } } private static void EnsureSeedData(ConfigurationDbContext context) { Console.WriteLine("Seeding database..."); if (!context.Clients.Any()) { Console.WriteLine("Clients being populated"); foreach (var client in Clients.Get()) { context.Clients.Add(client.ToEntity()); } context.SaveChanges(); } else { Console.WriteLine("Clients already populated"); } if (!context.IdentityResources.Any()) { Console.WriteLine("IdentityResources being populated"); foreach (var resource in Resources.IdentityResources) { context.IdentityResources.Add(resource.ToEntity()); } context.SaveChanges(); } else { Console.WriteLine("IdentityResources already populated"); } if (!context.ApiResources.Any()) { Console.WriteLine("ApiResources being populated"); foreach (var resource in Resources.ApiResources) { context.ApiResources.Add(resource.ToEntity()); } context.SaveChanges(); } else { Console.WriteLine("ApiResources already populated"); } if (!context.ApiScopes.Any()) { Console.WriteLine("Scopes being populated"); foreach (var resource in Resources.ApiScopes) { context.ApiScopes.Add(resource.ToEntity()); } context.SaveChanges(); } else { Console.WriteLine("Scopes already populated"); } Console.WriteLine("Done seeding database."); Console.WriteLine(); } } ================================================ FILE: src/EntityFramework/migrations/SqlServer/SqlServer.csproj ================================================ ================================================ FILE: src/EntityFramework/migrations/SqlServer/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore; namespace SqlServer; public class Startup { public IConfiguration Configuration { get; } public Startup(IConfiguration config) { Configuration = config; } public void ConfigureServices(IServiceCollection services) { var cn = Configuration.GetConnectionString("db"); services.AddIdentityServer() .AddConfigurationStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(cn); }) .AddOperationalStore(options => { options.ConfigureDbContext = b => b.UseSqlServer(cn); }); } public void Configure(IApplicationBuilder app) { } } ================================================ FILE: src/EntityFramework/migrations/SqlServer/appsettings.json ================================================ { "ConnectionStrings": { "db": "server=(localdb)\\mssqllocaldb;database=IdentityServer8.EntityFramework-8.0.0;trusted_connection=yes;", } } ================================================ FILE: src/EntityFramework/migrations.bat ================================================ cd host rmdir /S /Q Migrations dotnet ef migrations add Grants -c PersistedGrantDbContext -o Migrations/IdentityServer/PersistedGrantDb dotnet ef migrations add Config -c ConfigurationDbContext -o Migrations/IdentityServer/ConfigurationDb dotnet ef migrations script -c PersistedGrantDbContext -o Migrations/IdentityServer/PersistedGrantDb.sql dotnet ef migrations script -c ConfigurationDbContext -o Migrations/IdentityServer/ConfigurationDb.sql cd .. ================================================ FILE: src/EntityFramework/src/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityServer8.EntityFramework; global using IdentityServer8.EntityFramework.DbContexts; global using IdentityServer8.EntityFramework.Interfaces; global using IdentityServer8.EntityFramework.Options; global using IdentityServer8.EntityFramework.Services; global using IdentityServer8.EntityFramework.Storage; global using IdentityServer8.EntityFramework.Stores; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using Microsoft.AspNetCore.Http; global using Microsoft.EntityFrameworkCore; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Hosting; global using Microsoft.Extensions.Logging; ================================================ FILE: src/EntityFramework/src/IdentityServer8.EntityFramework.csproj ================================================ EntityFramework persistence layer for IdentityServer8 true true True True ================================================ FILE: src/EntityFramework/src/IdentityServerEntityFrameworkBuilderExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// Extension methods to add EF database support to IdentityServer. /// public static class IdentityServerEntityFrameworkBuilderExtensions { /// /// Configures EF implementation of IClientStore, IResourceStore, and ICorsPolicyService with IdentityServer. /// /// The builder. /// The store options action. /// public static IIdentityServerBuilder AddConfigurationStore( this IIdentityServerBuilder builder, Action storeOptionsAction = null) { return builder.AddConfigurationStore(storeOptionsAction); } /// /// Configures EF implementation of IClientStore, IResourceStore, and ICorsPolicyService with IdentityServer. /// /// The IConfigurationDbContext to use. /// The builder. /// The store options action. /// public static IIdentityServerBuilder AddConfigurationStore( this IIdentityServerBuilder builder, Action storeOptionsAction = null) where TContext : DbContext, IConfigurationDbContext { builder.Services.AddConfigurationDbContext(storeOptionsAction); builder.AddClientStore(); builder.AddResourceStore(); builder.AddCorsPolicyService(); return builder; } /// /// Configures caching for IClientStore, IResourceStore, and ICorsPolicyService with IdentityServer. /// /// The builder. /// public static IIdentityServerBuilder AddConfigurationStoreCache( this IIdentityServerBuilder builder) { builder.AddInMemoryCaching(); // add the caching decorators builder.AddClientStoreCache(); builder.AddResourceStoreCache(); builder.AddCorsPolicyCache(); return builder; } /// /// Configures EF implementation of IPersistedGrantStore with IdentityServer. /// /// The builder. /// The store options action. /// public static IIdentityServerBuilder AddOperationalStore( this IIdentityServerBuilder builder, Action storeOptionsAction = null) { return builder.AddOperationalStore(storeOptionsAction); } /// /// Configures EF implementation of IPersistedGrantStore with IdentityServer. /// /// The IPersistedGrantDbContext to use. /// The builder. /// The store options action. /// public static IIdentityServerBuilder AddOperationalStore( this IIdentityServerBuilder builder, Action storeOptionsAction = null) where TContext : DbContext, IPersistedGrantDbContext { builder.Services.AddOperationalDbContext(storeOptionsAction); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddSingleton(); return builder; } /// /// Adds an implementation of the IOperationalStoreNotification to IdentityServer. /// /// /// /// public static IIdentityServerBuilder AddOperationalStoreNotification( this IIdentityServerBuilder builder) where T : class, IOperationalStoreNotification { builder.Services.AddOperationalStoreNotification(); return builder; } } ================================================ FILE: src/EntityFramework/src/Services/CorsPolicyService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Services; /// /// Implementation of ICorsPolicyService that consults the client configuration in the database for allowed CORS origins. /// /// public class CorsPolicyService : ICorsPolicyService { private readonly IHttpContextAccessor _context; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The context. /// The logger. /// context public CorsPolicyService(IHttpContextAccessor context, ILogger logger) { _context = context ?? throw new ArgumentNullException(nameof(context)); _logger = logger; } /// /// Determines whether origin is allowed. /// /// The origin. /// public async Task IsOriginAllowedAsync(string origin) { origin = origin.ToLowerInvariant(); // doing this here and not in the ctor because: https://github.com/aspnet/CORS/issues/105 var dbContext = _context.HttpContext.RequestServices.GetRequiredService(); var query = from o in dbContext.ClientCorsOrigins where o.Origin == origin select o; var isAllowed = await query.AnyAsync(); _logger.LogDebug("Origin {origin} is allowed: {originAllowed}", Ioc.Sanitizer.Log.Sanitize(origin), isAllowed); return isAllowed; } } ================================================ FILE: src/EntityFramework/src/TokenCleanupHost.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// Helper to cleanup expired persisted grants. /// public class TokenCleanupHost : IHostedService { private readonly IServiceProvider _serviceProvider; private readonly OperationalStoreOptions _options; private readonly ILogger _logger; private TimeSpan CleanupInterval => TimeSpan.FromSeconds(_options.TokenCleanupInterval); private CancellationTokenSource _source; /// /// Constructor for TokenCleanupHost. /// /// /// /// public TokenCleanupHost(IServiceProvider serviceProvider, OperationalStoreOptions options, ILogger logger) { _serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); _options = options ?? throw new ArgumentNullException(nameof(options)); _logger = logger; } /// /// Starts the token cleanup polling. /// public Task StartAsync(CancellationToken cancellationToken) { if (_options.EnableTokenCleanup) { if (_source != null) throw new InvalidOperationException("Already started. Call Stop first."); _logger.LogDebug("Starting grant removal"); _source = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken); Task.Factory.StartNew(() => StartInternalAsync(_source.Token)); } return Task.CompletedTask; } /// /// Stops the token cleanup polling. /// public Task StopAsync(CancellationToken cancellationToken) { if (_options.EnableTokenCleanup) { if (_source == null) throw new InvalidOperationException("Not started. Call Start first."); _logger.LogDebug("Stopping grant removal"); _source.Cancel(); _source = null; } return Task.CompletedTask; } private async Task StartInternalAsync(CancellationToken cancellationToken) { while (true) { if (cancellationToken.IsCancellationRequested) { _logger.LogDebug("CancellationRequested. Exiting."); break; } try { await Task.Delay(CleanupInterval, cancellationToken); } catch (TaskCanceledException) { _logger.LogDebug("TaskCanceledException. Exiting."); break; } catch (Exception ex) { _logger.LogError("Task.Delay exception: {0}. Exiting.", ex.Message); break; } if (cancellationToken.IsCancellationRequested) { _logger.LogDebug("CancellationRequested. Exiting."); break; } await RemoveExpiredGrantsAsync(); } } async Task RemoveExpiredGrantsAsync() { try { using (var serviceScope = _serviceProvider.GetRequiredService().CreateScope()) { var tokenCleanupService = serviceScope.ServiceProvider.GetRequiredService(); await tokenCleanupService.RemoveExpiredGrantsAsync(); } } catch (Exception ex) { _logger.LogError("Exception removing expired grants: {exception}", ex.Message); } } } ================================================ FILE: src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/DatabaseProviderBuilder.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore; namespace IdentityServer8.EntityFramework.IntegrationTests; /// /// Helper methods to initialize DbContextOptions for the specified database provider and context. /// public class DatabaseProviderBuilder { public static DbContextOptions BuildInMemory(string name) where T : DbContext { var builder = new DbContextOptionsBuilder(); builder.UseInMemoryDatabase(name); return builder.Options; } public static DbContextOptions BuildSqlite(string name) where T : DbContext { var builder = new DbContextOptionsBuilder(); builder.UseSqlite($"Filename=./Test.IdentityServer8.EntityFramework-3.1.0.{name}.db"); return builder.Options; } public static DbContextOptions BuildLocalDb(string name) where T : DbContext { var builder = new DbContextOptionsBuilder(); builder.UseSqlServer( $@"Data Source=(LocalDb)\MSSQLLocalDB;database=Test.IdentityServer8.EntityFramework-3.1.0.{name};trusted_connection=yes;"); return builder.Options; } public static DbContextOptions BuildAppVeyorSqlServer2016(string name) where T : DbContext { var builder = new DbContextOptionsBuilder(); builder.UseSqlServer($@"Server=(local)\SQL2016;Database=Test.IdentityServer8.EntityFramework-3.1.0.{name};User ID=sa;Password=Password12!"); return builder.Options; } } ================================================ FILE: src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/DatabaseProviderFixture.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using Microsoft.EntityFrameworkCore; namespace IdentityServer8.EntityFramework.IntegrationTests; /// /// xUnit ClassFixture for creating and deleting integration test databases. /// /// DbContext of Type T public class DatabaseProviderFixture : IDisposable where T : DbContext { public object StoreOptions; public List> Options; public void Dispose() { if (Options != null) // null check since fixtures are created even when tests are skipped { foreach (var option in Options.ToList()) { using (var context = (T)Activator.CreateInstance(typeof(T), option, StoreOptions)) { context.Database.EnsureDeleted(); } } } } } ================================================ FILE: src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/FakeLogger.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.Extensions.Logging; namespace IdentityServer8.EntityFramework.IntegrationTests; public class FakeLogger : FakeLogger, ILogger { public static ILogger Create() { return new FakeLogger(); } } public class FakeLogger : ILogger, IDisposable { public IDisposable BeginScope(TState state) { return this; } public void Dispose() { } public bool IsEnabled(LogLevel logLevel) { return false; } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { } } ================================================ FILE: src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/IdentityServer8.EntityFramework.Tests.csproj ================================================ false runtime; build; native; contentfiles; analyzers; buildtransitive all ================================================ FILE: src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/IntegrationTest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using System.Runtime.InteropServices; using Xunit; namespace IdentityServer8.EntityFramework.IntegrationTests; /// /// Base class for integration tests, responsible for initializing test database providers & an xUnit class fixture /// /// The type of the class. /// The type of the database context. /// The type of the store option. /// public class IntegrationTest : IClassFixture> where TDbContext : DbContext { public static readonly TheoryData> TestDatabaseProviders; protected readonly TStoreOption StoreOptions = Activator.CreateInstance(); static IntegrationTest() { var config = new ConfigurationBuilder() .AddEnvironmentVariables() .Build(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.WriteLine($"Running Local Tests for {typeof(TClass).Name}"); TestDatabaseProviders = new TheoryData> { DatabaseProviderBuilder.BuildInMemory(typeof(TClass).Name), DatabaseProviderBuilder.BuildSqlite(typeof(TClass).Name), DatabaseProviderBuilder.BuildLocalDb(typeof(TClass).Name) }; } else { TestDatabaseProviders = new TheoryData> { DatabaseProviderBuilder.BuildInMemory(typeof(TClass).Name), DatabaseProviderBuilder.BuildSqlite(typeof(TClass).Name) }; Console.WriteLine("Skipping DB integration tests on non-Windows"); } } protected IntegrationTest(DatabaseProviderFixture fixture) { fixture.Options = TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList(); fixture.StoreOptions = StoreOptions; } } ================================================ FILE: src/EntityFramework/test/IdentityServer8.EntityFramework.Tests/Services/CorsPolicyServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.DbContexts; using IdentityServer8.EntityFramework.Interfaces; using IdentityServer8.EntityFramework.Mappers; using IdentityServer8.EntityFramework.Options; using IdentityServer8.EntityFramework.Services; using IdentityServer8.Models; using Microsoft.AspNetCore.Http; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer8.EntityFramework.IntegrationTests.Services; public class CorsPolicyServiceTests : IntegrationTest { public CorsPolicyServiceTests(DatabaseProviderFixture fixture) : base(fixture) { foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList()) { using (var context = new ConfigurationDbContext(options, StoreOptions)) context.Database.EnsureCreated(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task IsOriginAllowedAsync_WhenOriginIsAllowed_ExpectTrue(DbContextOptions options) { const string testCorsOrigin = "https://identityserver8.io/"; using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.Clients.Add(new Client { ClientId = Guid.NewGuid().ToString(), ClientName = Guid.NewGuid().ToString(), AllowedCorsOrigins = new List { "https://www.identityserver8.com" } }.ToEntity()); context.Clients.Add(new Client { ClientId = "2", ClientName = "2", AllowedCorsOrigins = new List { "https://www.identityserver8.com", testCorsOrigin } }.ToEntity()); context.SaveChanges(); } bool result; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var ctx = new DefaultHttpContext(); var svcs = new ServiceCollection(); svcs.AddSingleton(context); ctx.RequestServices = svcs.BuildServiceProvider(); var ctxAccessor = new HttpContextAccessor(); ctxAccessor.HttpContext = ctx; var service = new CorsPolicyService(ctxAccessor, FakeLogger.Create()); result = await service.IsOriginAllowedAsync(testCorsOrigin); } Assert.True(result); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task IsOriginAllowedAsync_WhenOriginIsNotAllowed_ExpectFalse(DbContextOptions options) { using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.Clients.Add(new Client { ClientId = Guid.NewGuid().ToString(), ClientName = Guid.NewGuid().ToString(), AllowedCorsOrigins = new List { "https://www.identityserver8.com" } }.ToEntity()); context.SaveChanges(); } bool result; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var ctx = new DefaultHttpContext(); var svcs = new ServiceCollection(); svcs.AddSingleton(context); ctx.RequestServices = svcs.BuildServiceProvider(); var ctxAccessor = new HttpContextAccessor(); ctxAccessor.HttpContext = ctx; var service = new CorsPolicyService(ctxAccessor, FakeLogger.Create()); result = await service.IsOriginAllowedAsync("InvalidOrigin"); } Assert.False(result); } } ================================================ FILE: src/EntityFramework/updatedb.bat ================================================ cd host dotnet ef database update -c PersistedGrantDbContext dotnet ef database update -c ConfigurationDbContext dotnet run /seed cd .. ================================================ FILE: src/EntityFramework.Storage/Directory.Build.props ================================================ ================================================ FILE: src/EntityFramework.Storage/IdentityServer8.EntityFramework.Storage.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 16 VisualStudioVersion = 16.0.30204.135 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{AF5DAC33-08AC-45EE-9772-4FF39FB09E57}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework.Storage", "src\IdentityServer8.EntityFramework.Storage.csproj", "{5302DAB3-1662-4956-97AA-5EA5E85B10F6}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{712ED94A-F982-4667-A9CE-E8F21900BBED}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework.UnitTests", "test\UnitTests\IdentityServer8.EntityFramework.UnitTests.csproj", "{8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework.IntegrationTests", "test\IntegrationTests\IdentityServer8.EntityFramework.IntegrationTests.csproj", "{E90F7470-C7F8-464B-9C28-87C474085812}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "migrations", "migrations", "{E3EF31E0-6658-4899-8BDA-FF84355E2FDD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlServer", "migrations\SqlServer\SqlServer.csproj", "{A93A59EC-D75D-44E1-9720-F75D9EF95BC3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "host", "host", "{07C56E10-A807-4372-ACD9-ADED2D099BC8}" EndProject Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ConsoleHost", "host\ConsoleHost\ConsoleHost.csproj", "{2AA5AC6B-531B-426E-AD38-5B03F1949CF5}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Debug|x64.ActiveCfg = Debug|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Debug|x64.Build.0 = Debug|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Debug|x86.ActiveCfg = Debug|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Debug|x86.Build.0 = Debug|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Release|Any CPU.Build.0 = Release|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Release|x64.ActiveCfg = Release|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Release|x64.Build.0 = Release|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Release|x86.ActiveCfg = Release|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Release|x86.Build.0 = Release|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Debug|Any CPU.Build.0 = Debug|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Debug|x64.ActiveCfg = Debug|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Debug|x64.Build.0 = Debug|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Debug|x86.ActiveCfg = Debug|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Debug|x86.Build.0 = Debug|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Release|Any CPU.Build.0 = Release|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Release|x64.ActiveCfg = Release|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Release|x64.Build.0 = Release|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Release|x86.ActiveCfg = Release|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Release|x86.Build.0 = Release|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Debug|Any CPU.Build.0 = Debug|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Debug|x64.ActiveCfg = Debug|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Debug|x64.Build.0 = Debug|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Debug|x86.ActiveCfg = Debug|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Debug|x86.Build.0 = Debug|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Release|Any CPU.ActiveCfg = Release|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Release|Any CPU.Build.0 = Release|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Release|x64.ActiveCfg = Release|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Release|x64.Build.0 = Release|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Release|x86.ActiveCfg = Release|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Release|x86.Build.0 = Release|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Debug|Any CPU.Build.0 = Debug|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Debug|x64.ActiveCfg = Debug|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Debug|x64.Build.0 = Debug|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Debug|x86.ActiveCfg = Debug|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Debug|x86.Build.0 = Debug|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Release|Any CPU.ActiveCfg = Release|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Release|Any CPU.Build.0 = Release|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Release|x64.ActiveCfg = Release|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Release|x64.Build.0 = Release|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Release|x86.ActiveCfg = Release|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Release|x86.Build.0 = Release|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Debug|Any CPU.Build.0 = Debug|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Debug|x64.ActiveCfg = Debug|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Debug|x64.Build.0 = Debug|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Debug|x86.ActiveCfg = Debug|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Debug|x86.Build.0 = Debug|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Release|Any CPU.Build.0 = Release|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Release|x64.ActiveCfg = Release|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Release|x64.Build.0 = Release|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Release|x86.ActiveCfg = Release|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {5302DAB3-1662-4956-97AA-5EA5E85B10F6} = {AF5DAC33-08AC-45EE-9772-4FF39FB09E57} {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC} = {712ED94A-F982-4667-A9CE-E8F21900BBED} {E90F7470-C7F8-464B-9C28-87C474085812} = {712ED94A-F982-4667-A9CE-E8F21900BBED} {A93A59EC-D75D-44E1-9720-F75D9EF95BC3} = {E3EF31E0-6658-4899-8BDA-FF84355E2FDD} {2AA5AC6B-531B-426E-AD38-5B03F1949CF5} = {07C56E10-A807-4372-ACD9-ADED2D099BC8} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {4DB894E3-1BF2-4410-911A-14D32FD79A96} EndGlobalSection EndGlobal ================================================ FILE: src/EntityFramework.Storage/README.md ================================================ # IdentityServer8.EntityFramework.Storage IdentityServer8.EntityFramework.Storage is a persistence layer for IdentityServer 4 configuration data that uses EntityFramework as its database abstraction. ## Issues For issues, use the [consolidated IdentityServer8 issue tracker](https://github.com/alexhiggins732/IdentityServer8/issues). ================================================ FILE: src/EntityFramework.Storage/build.cmd ================================================ @echo off dotnet run --project build -- %* ================================================ FILE: src/EntityFramework.Storage/build.ps1 ================================================ $ErrorActionPreference = "Stop"; dotnet run --project build -- $args ================================================ FILE: src/EntityFramework.Storage/build.sh ================================================ #!/usr/bin/env bash set -euo pipefail dotnet run --project build -- "$@" ================================================ FILE: src/EntityFramework.Storage/host/ConsoleHost/ConsoleHost.csproj ================================================ Exe ================================================ FILE: src/EntityFramework.Storage/host/ConsoleHost/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityServer8.EntityFramework; global using IdentityServer8.EntityFramework.Storage; global using Microsoft.EntityFrameworkCore; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Logging; ================================================ FILE: src/EntityFramework.Storage/host/ConsoleHost/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace ConsoleHost; class Program { static void Main(string[] args) { var connectionString = "server=(localdb)\\mssqllocaldb;database=IdentityServer8.EntityFramework-8.0.0;trusted_connection=yes;"; var services = new ServiceCollection(); services.AddLogging(b => b.AddConsole().SetMinimumLevel(LogLevel.Trace)); services.AddOperationalDbContext(options => { options.ConfigureDbContext = builder => builder.UseSqlServer(connectionString); // this enables automatic token cleanup. this is optional. options.EnableTokenCleanup = false; options.TokenCleanupInterval = 5; // interval in seconds, short for testing }); var sp = services.BuildServiceProvider(); using (var scope = sp.CreateScope()) { var svc = scope.ServiceProvider.GetRequiredService(); svc.RemoveExpiredGrantsAsync().GetAwaiter().GetResult(); } } } ================================================ FILE: src/EntityFramework.Storage/host/Directory.Build.props ================================================ ================================================ FILE: src/EntityFramework.Storage/migrations/Directory.Build.props ================================================ ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityServer8.EntityFramework.DbContexts; global using IdentityServer8.EntityFramework.Storage; global using Microsoft.AspNetCore; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.Infrastructure; global using Microsoft.EntityFrameworkCore.Metadata; global using Microsoft.EntityFrameworkCore.Migrations; ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Migrations/ConfigurationDb/20200522172542_Config.Designer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // namespace SqlServer.Migrations.ConfigurationDb { [DbContext(typeof(ConfigurationDbContext))] [Migration("20200522172542_Config")] partial class Config { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.0") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AllowedAccessTokenSigningAlgorithms") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Enabled") .HasColumnType("bit"); b.Property("LastAccessed") .HasColumnType("datetime2"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("NonEditable") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("ApiResources"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Scope") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(4000)") .HasMaxLength(4000); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceSecrets"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Emphasize") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Required") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("ApiScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ScopeId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ScopeId"); b.ToTable("ApiScopeClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("ScopeId") .HasColumnType("int"); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ScopeId"); b.ToTable("ApiScopeProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AbsoluteRefreshTokenLifetime") .HasColumnType("int"); b.Property("AccessTokenLifetime") .HasColumnType("int"); b.Property("AccessTokenType") .HasColumnType("int"); b.Property("AllowAccessTokensViaBrowser") .HasColumnType("bit"); b.Property("AllowOfflineAccess") .HasColumnType("bit"); b.Property("AllowPlainTextPkce") .HasColumnType("bit"); b.Property("AllowRememberConsent") .HasColumnType("bit"); b.Property("AllowedIdentityTokenSigningAlgorithms") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("AlwaysIncludeUserClaimsInIdToken") .HasColumnType("bit"); b.Property("AlwaysSendClientClaims") .HasColumnType("bit"); b.Property("AuthorizationCodeLifetime") .HasColumnType("int"); b.Property("BackChannelLogoutSessionRequired") .HasColumnType("bit"); b.Property("BackChannelLogoutUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("ClientClaimsPrefix") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("ConsentLifetime") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DeviceCodeLifetime") .HasColumnType("int"); b.Property("EnableLocalLogin") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("FrontChannelLogoutSessionRequired") .HasColumnType("bit"); b.Property("FrontChannelLogoutUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("IdentityTokenLifetime") .HasColumnType("int"); b.Property("IncludeJwtId") .HasColumnType("bit"); b.Property("LastAccessed") .HasColumnType("datetime2"); b.Property("LogoUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("NonEditable") .HasColumnType("bit"); b.Property("PairWiseSubjectSalt") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ProtocolType") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("RefreshTokenExpiration") .HasColumnType("int"); b.Property("RefreshTokenUsage") .HasColumnType("int"); b.Property("RequireClientSecret") .HasColumnType("bit"); b.Property("RequireConsent") .HasColumnType("bit"); b.Property("RequirePkce") .HasColumnType("bit"); b.Property("RequireRequestObject") .HasColumnType("bit"); b.Property("SlidingRefreshTokenLifetime") .HasColumnType("int"); b.Property("UpdateAccessTokenClaimsOnRefresh") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.Property("UserCodeType") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("UserSsoLifetime") .HasColumnType("int"); b.HasKey("Id"); b.HasIndex("ClientId") .IsUnique(); b.ToTable("Clients"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Origin") .IsRequired() .HasColumnType("nvarchar(150)") .HasMaxLength(150); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientCorsOrigins"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("GrantType") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientGrantTypes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Provider") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientIdPRestrictions"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("PostLogoutRedirectUri") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientPostLogoutRedirectUris"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("RedirectUri") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientRedirectUris"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Scope") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(4000)") .HasMaxLength(4000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientSecrets"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Emphasize") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("NonEditable") .HasColumnType("bit"); b.Property("Required") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("IdentityResources"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("IdentityResourceId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("IdentityResourceId"); b.ToTable("IdentityResourceClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("IdentityResourceId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("IdentityResourceId"); b.ToTable("IdentityResourceProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("UserClaims") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Properties") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Scopes") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Secrets") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") .WithMany("UserClaims") .HasForeignKey("ScopeId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") .WithMany("Properties") .HasForeignKey("ScopeId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("Claims") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedCorsOrigins") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedGrantTypes") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("IdentityProviderRestrictions") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("PostLogoutRedirectUris") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("Properties") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("RedirectUris") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedScopes") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("ClientSecrets") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") .WithMany("UserClaims") .HasForeignKey("IdentityResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") .WithMany("Properties") .HasForeignKey("IdentityResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); #pragma warning restore 612, 618 } } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Migrations/ConfigurationDb/20200522172542_Config.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace SqlServer.Migrations.ConfigurationDb; public partial class Config : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "ApiResources", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Enabled = table.Column(nullable: false), Name = table.Column(maxLength: 200, nullable: false), DisplayName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), AllowedAccessTokenSigningAlgorithms = table.Column(maxLength: 100, nullable: true), ShowInDiscoveryDocument = table.Column(nullable: false), Created = table.Column(nullable: false), Updated = table.Column(nullable: true), LastAccessed = table.Column(nullable: true), NonEditable = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResources", x => x.Id); }); migrationBuilder.CreateTable( name: "ApiScopes", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Enabled = table.Column(nullable: false), Name = table.Column(maxLength: 200, nullable: false), DisplayName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), Required = table.Column(nullable: false), Emphasize = table.Column(nullable: false), ShowInDiscoveryDocument = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiScopes", x => x.Id); }); migrationBuilder.CreateTable( name: "Clients", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Enabled = table.Column(nullable: false), ClientId = table.Column(maxLength: 200, nullable: false), ProtocolType = table.Column(maxLength: 200, nullable: false), RequireClientSecret = table.Column(nullable: false), ClientName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), ClientUri = table.Column(maxLength: 2000, nullable: true), LogoUri = table.Column(maxLength: 2000, nullable: true), RequireConsent = table.Column(nullable: false), AllowRememberConsent = table.Column(nullable: false), AlwaysIncludeUserClaimsInIdToken = table.Column(nullable: false), RequirePkce = table.Column(nullable: false), AllowPlainTextPkce = table.Column(nullable: false), RequireRequestObject = table.Column(nullable: false), AllowAccessTokensViaBrowser = table.Column(nullable: false), FrontChannelLogoutUri = table.Column(maxLength: 2000, nullable: true), FrontChannelLogoutSessionRequired = table.Column(nullable: false), BackChannelLogoutUri = table.Column(maxLength: 2000, nullable: true), BackChannelLogoutSessionRequired = table.Column(nullable: false), AllowOfflineAccess = table.Column(nullable: false), IdentityTokenLifetime = table.Column(nullable: false), AllowedIdentityTokenSigningAlgorithms = table.Column(maxLength: 100, nullable: true), AccessTokenLifetime = table.Column(nullable: false), AuthorizationCodeLifetime = table.Column(nullable: false), ConsentLifetime = table.Column(nullable: true), AbsoluteRefreshTokenLifetime = table.Column(nullable: false), SlidingRefreshTokenLifetime = table.Column(nullable: false), RefreshTokenUsage = table.Column(nullable: false), UpdateAccessTokenClaimsOnRefresh = table.Column(nullable: false), RefreshTokenExpiration = table.Column(nullable: false), AccessTokenType = table.Column(nullable: false), EnableLocalLogin = table.Column(nullable: false), IncludeJwtId = table.Column(nullable: false), AlwaysSendClientClaims = table.Column(nullable: false), ClientClaimsPrefix = table.Column(maxLength: 200, nullable: true), PairWiseSubjectSalt = table.Column(maxLength: 200, nullable: true), Created = table.Column(nullable: false), Updated = table.Column(nullable: true), LastAccessed = table.Column(nullable: true), UserSsoLifetime = table.Column(nullable: true), UserCodeType = table.Column(maxLength: 100, nullable: true), DeviceCodeLifetime = table.Column(nullable: false), NonEditable = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_Clients", x => x.Id); }); migrationBuilder.CreateTable( name: "IdentityResources", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Enabled = table.Column(nullable: false), Name = table.Column(maxLength: 200, nullable: false), DisplayName = table.Column(maxLength: 200, nullable: true), Description = table.Column(maxLength: 1000, nullable: true), Required = table.Column(nullable: false), Emphasize = table.Column(nullable: false), ShowInDiscoveryDocument = table.Column(nullable: false), Created = table.Column(nullable: false), Updated = table.Column(nullable: true), NonEditable = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_IdentityResources", x => x.Id); }); migrationBuilder.CreateTable( name: "ApiResourceClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Type = table.Column(maxLength: 200, nullable: false), ApiResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResourceClaims", x => x.Id); table.ForeignKey( name: "FK_ApiResourceClaims_ApiResources_ApiResourceId", column: x => x.ApiResourceId, principalTable: "ApiResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiResourceProperties", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Key = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 2000, nullable: false), ApiResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResourceProperties", x => x.Id); table.ForeignKey( name: "FK_ApiResourceProperties_ApiResources_ApiResourceId", column: x => x.ApiResourceId, principalTable: "ApiResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiResourceScopes", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Scope = table.Column(maxLength: 200, nullable: false), ApiResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResourceScopes", x => x.Id); table.ForeignKey( name: "FK_ApiResourceScopes_ApiResources_ApiResourceId", column: x => x.ApiResourceId, principalTable: "ApiResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiResourceSecrets", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Description = table.Column(maxLength: 1000, nullable: true), Value = table.Column(maxLength: 4000, nullable: false), Expiration = table.Column(nullable: true), Type = table.Column(maxLength: 250, nullable: false), Created = table.Column(nullable: false), ApiResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiResourceSecrets", x => x.Id); table.ForeignKey( name: "FK_ApiResourceSecrets_ApiResources_ApiResourceId", column: x => x.ApiResourceId, principalTable: "ApiResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiScopeClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Type = table.Column(maxLength: 200, nullable: false), ScopeId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiScopeClaims", x => x.Id); table.ForeignKey( name: "FK_ApiScopeClaims_ApiScopes_ScopeId", column: x => x.ScopeId, principalTable: "ApiScopes", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ApiScopeProperties", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Key = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 2000, nullable: false), ScopeId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ApiScopeProperties", x => x.Id); table.ForeignKey( name: "FK_ApiScopeProperties_ApiScopes_ScopeId", column: x => x.ScopeId, principalTable: "ApiScopes", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Type = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 250, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientClaims", x => x.Id); table.ForeignKey( name: "FK_ClientClaims_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientCorsOrigins", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Origin = table.Column(maxLength: 150, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientCorsOrigins", x => x.Id); table.ForeignKey( name: "FK_ClientCorsOrigins_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientGrantTypes", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), GrantType = table.Column(maxLength: 250, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientGrantTypes", x => x.Id); table.ForeignKey( name: "FK_ClientGrantTypes_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientIdPRestrictions", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Provider = table.Column(maxLength: 200, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientIdPRestrictions", x => x.Id); table.ForeignKey( name: "FK_ClientIdPRestrictions_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientPostLogoutRedirectUris", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), PostLogoutRedirectUri = table.Column(maxLength: 2000, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientPostLogoutRedirectUris", x => x.Id); table.ForeignKey( name: "FK_ClientPostLogoutRedirectUris_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientProperties", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Key = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 2000, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientProperties", x => x.Id); table.ForeignKey( name: "FK_ClientProperties_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientRedirectUris", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), RedirectUri = table.Column(maxLength: 2000, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientRedirectUris", x => x.Id); table.ForeignKey( name: "FK_ClientRedirectUris_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientScopes", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Scope = table.Column(maxLength: 200, nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientScopes", x => x.Id); table.ForeignKey( name: "FK_ClientScopes_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "ClientSecrets", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Description = table.Column(maxLength: 2000, nullable: true), Value = table.Column(maxLength: 4000, nullable: false), Expiration = table.Column(nullable: true), Type = table.Column(maxLength: 250, nullable: false), Created = table.Column(nullable: false), ClientId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_ClientSecrets", x => x.Id); table.ForeignKey( name: "FK_ClientSecrets_Clients_ClientId", column: x => x.ClientId, principalTable: "Clients", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "IdentityResourceClaims", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Type = table.Column(maxLength: 200, nullable: false), IdentityResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_IdentityResourceClaims", x => x.Id); table.ForeignKey( name: "FK_IdentityResourceClaims_IdentityResources_IdentityResourceId", column: x => x.IdentityResourceId, principalTable: "IdentityResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateTable( name: "IdentityResourceProperties", columns: table => new { Id = table.Column(nullable: false) .Annotation("SqlServer:Identity", "1, 1"), Key = table.Column(maxLength: 250, nullable: false), Value = table.Column(maxLength: 2000, nullable: false), IdentityResourceId = table.Column(nullable: false) }, constraints: table => { table.PrimaryKey("PK_IdentityResourceProperties", x => x.Id); table.ForeignKey( name: "FK_IdentityResourceProperties_IdentityResources_IdentityResourceId", column: x => x.IdentityResourceId, principalTable: "IdentityResources", principalColumn: "Id", onDelete: ReferentialAction.Cascade); }); migrationBuilder.CreateIndex( name: "IX_ApiResourceClaims_ApiResourceId", table: "ApiResourceClaims", column: "ApiResourceId"); migrationBuilder.CreateIndex( name: "IX_ApiResourceProperties_ApiResourceId", table: "ApiResourceProperties", column: "ApiResourceId"); migrationBuilder.CreateIndex( name: "IX_ApiResources_Name", table: "ApiResources", column: "Name", unique: true); migrationBuilder.CreateIndex( name: "IX_ApiResourceScopes_ApiResourceId", table: "ApiResourceScopes", column: "ApiResourceId"); migrationBuilder.CreateIndex( name: "IX_ApiResourceSecrets_ApiResourceId", table: "ApiResourceSecrets", column: "ApiResourceId"); migrationBuilder.CreateIndex( name: "IX_ApiScopeClaims_ScopeId", table: "ApiScopeClaims", column: "ScopeId"); migrationBuilder.CreateIndex( name: "IX_ApiScopeProperties_ScopeId", table: "ApiScopeProperties", column: "ScopeId"); migrationBuilder.CreateIndex( name: "IX_ApiScopes_Name", table: "ApiScopes", column: "Name", unique: true); migrationBuilder.CreateIndex( name: "IX_ClientClaims_ClientId", table: "ClientClaims", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientCorsOrigins_ClientId", table: "ClientCorsOrigins", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientGrantTypes_ClientId", table: "ClientGrantTypes", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientIdPRestrictions_ClientId", table: "ClientIdPRestrictions", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientPostLogoutRedirectUris_ClientId", table: "ClientPostLogoutRedirectUris", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientProperties_ClientId", table: "ClientProperties", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientRedirectUris_ClientId", table: "ClientRedirectUris", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_Clients_ClientId", table: "Clients", column: "ClientId", unique: true); migrationBuilder.CreateIndex( name: "IX_ClientScopes_ClientId", table: "ClientScopes", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_ClientSecrets_ClientId", table: "ClientSecrets", column: "ClientId"); migrationBuilder.CreateIndex( name: "IX_IdentityResourceClaims_IdentityResourceId", table: "IdentityResourceClaims", column: "IdentityResourceId"); migrationBuilder.CreateIndex( name: "IX_IdentityResourceProperties_IdentityResourceId", table: "IdentityResourceProperties", column: "IdentityResourceId"); migrationBuilder.CreateIndex( name: "IX_IdentityResources_Name", table: "IdentityResources", column: "Name", unique: true); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "ApiResourceClaims"); migrationBuilder.DropTable( name: "ApiResourceProperties"); migrationBuilder.DropTable( name: "ApiResourceScopes"); migrationBuilder.DropTable( name: "ApiResourceSecrets"); migrationBuilder.DropTable( name: "ApiScopeClaims"); migrationBuilder.DropTable( name: "ApiScopeProperties"); migrationBuilder.DropTable( name: "ClientClaims"); migrationBuilder.DropTable( name: "ClientCorsOrigins"); migrationBuilder.DropTable( name: "ClientGrantTypes"); migrationBuilder.DropTable( name: "ClientIdPRestrictions"); migrationBuilder.DropTable( name: "ClientPostLogoutRedirectUris"); migrationBuilder.DropTable( name: "ClientProperties"); migrationBuilder.DropTable( name: "ClientRedirectUris"); migrationBuilder.DropTable( name: "ClientScopes"); migrationBuilder.DropTable( name: "ClientSecrets"); migrationBuilder.DropTable( name: "IdentityResourceClaims"); migrationBuilder.DropTable( name: "IdentityResourceProperties"); migrationBuilder.DropTable( name: "ApiResources"); migrationBuilder.DropTable( name: "ApiScopes"); migrationBuilder.DropTable( name: "Clients"); migrationBuilder.DropTable( name: "IdentityResources"); } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Migrations/ConfigurationDb/ConfigurationDbContextModelSnapshot.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // namespace SqlServer.Migrations.ConfigurationDb; [DbContext(typeof(ConfigurationDbContext))] partial class ConfigurationDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.0") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResource", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AllowedAccessTokenSigningAlgorithms") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Enabled") .HasColumnType("bit"); b.Property("LastAccessed") .HasColumnType("datetime2"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("NonEditable") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("ApiResources"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Scope") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ApiResourceId") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(4000)") .HasMaxLength(4000); b.HasKey("Id"); b.HasIndex("ApiResourceId"); b.ToTable("ApiResourceSecrets"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Emphasize") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Required") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("ApiScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ScopeId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ScopeId"); b.ToTable("ApiScopeClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("ScopeId") .HasColumnType("int"); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ScopeId"); b.ToTable("ApiScopeProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.Client", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("AbsoluteRefreshTokenLifetime") .HasColumnType("int"); b.Property("AccessTokenLifetime") .HasColumnType("int"); b.Property("AccessTokenType") .HasColumnType("int"); b.Property("AllowAccessTokensViaBrowser") .HasColumnType("bit"); b.Property("AllowOfflineAccess") .HasColumnType("bit"); b.Property("AllowPlainTextPkce") .HasColumnType("bit"); b.Property("AllowRememberConsent") .HasColumnType("bit"); b.Property("AllowedIdentityTokenSigningAlgorithms") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("AlwaysIncludeUserClaimsInIdToken") .HasColumnType("bit"); b.Property("AlwaysSendClientClaims") .HasColumnType("bit"); b.Property("AuthorizationCodeLifetime") .HasColumnType("int"); b.Property("BackChannelLogoutSessionRequired") .HasColumnType("bit"); b.Property("BackChannelLogoutUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("ClientClaimsPrefix") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("ConsentLifetime") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DeviceCodeLifetime") .HasColumnType("int"); b.Property("EnableLocalLogin") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("FrontChannelLogoutSessionRequired") .HasColumnType("bit"); b.Property("FrontChannelLogoutUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("IdentityTokenLifetime") .HasColumnType("int"); b.Property("IncludeJwtId") .HasColumnType("bit"); b.Property("LastAccessed") .HasColumnType("datetime2"); b.Property("LogoUri") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("NonEditable") .HasColumnType("bit"); b.Property("PairWiseSubjectSalt") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ProtocolType") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("RefreshTokenExpiration") .HasColumnType("int"); b.Property("RefreshTokenUsage") .HasColumnType("int"); b.Property("RequireClientSecret") .HasColumnType("bit"); b.Property("RequireConsent") .HasColumnType("bit"); b.Property("RequirePkce") .HasColumnType("bit"); b.Property("RequireRequestObject") .HasColumnType("bit"); b.Property("SlidingRefreshTokenLifetime") .HasColumnType("int"); b.Property("UpdateAccessTokenClaimsOnRefresh") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.Property("UserCodeType") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("UserSsoLifetime") .HasColumnType("int"); b.HasKey("Id"); b.HasIndex("ClientId") .IsUnique(); b.ToTable("Clients"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Origin") .IsRequired() .HasColumnType("nvarchar(150)") .HasMaxLength(150); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientCorsOrigins"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("GrantType") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientGrantTypes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Provider") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientIdPRestrictions"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("PostLogoutRedirectUri") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientPostLogoutRedirectUris"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("RedirectUri") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientRedirectUris"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Scope") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientScopes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("ClientId") .HasColumnType("int"); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(4000)") .HasMaxLength(4000); b.HasKey("Id"); b.HasIndex("ClientId"); b.ToTable("ClientSecrets"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResource", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("Created") .HasColumnType("datetime2"); b.Property("Description") .HasColumnType("nvarchar(1000)") .HasMaxLength(1000); b.Property("DisplayName") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Emphasize") .HasColumnType("bit"); b.Property("Enabled") .HasColumnType("bit"); b.Property("Name") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("NonEditable") .HasColumnType("bit"); b.Property("Required") .HasColumnType("bit"); b.Property("ShowInDiscoveryDocument") .HasColumnType("bit"); b.Property("Updated") .HasColumnType("datetime2"); b.HasKey("Id"); b.HasIndex("Name") .IsUnique(); b.ToTable("IdentityResources"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("IdentityResourceId") .HasColumnType("int"); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("Id"); b.HasIndex("IdentityResourceId"); b.ToTable("IdentityResourceClaims"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => { b.Property("Id") .ValueGeneratedOnAdd() .HasColumnType("int") .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); b.Property("IdentityResourceId") .HasColumnType("int"); b.Property("Key") .IsRequired() .HasColumnType("nvarchar(250)") .HasMaxLength(250); b.Property("Value") .IsRequired() .HasColumnType("nvarchar(2000)") .HasMaxLength(2000); b.HasKey("Id"); b.HasIndex("IdentityResourceId"); b.ToTable("IdentityResourceProperties"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("UserClaims") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Properties") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceScope", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Scopes") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiResourceSecret", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiResource", "ApiResource") .WithMany("Secrets") .HasForeignKey("ApiResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") .WithMany("UserClaims") .HasForeignKey("ScopeId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ApiScopeProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.ApiScope", "Scope") .WithMany("Properties") .HasForeignKey("ScopeId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("Claims") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientCorsOrigin", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedCorsOrigins") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientGrantType", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedGrantTypes") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientIdPRestriction", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("IdentityProviderRestrictions") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientPostLogoutRedirectUri", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("PostLogoutRedirectUris") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("Properties") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientRedirectUri", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("RedirectUris") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientScope", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("AllowedScopes") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.ClientSecret", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.Client", "Client") .WithMany("ClientSecrets") .HasForeignKey("ClientId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceClaim", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") .WithMany("UserClaims") .HasForeignKey("IdentityResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.IdentityResourceProperty", b => { b.HasOne("IdentityServer8.EntityFramework.Entities.IdentityResource", "IdentityResource") .WithMany("Properties") .HasForeignKey("IdentityResourceId") .OnDelete(DeleteBehavior.Cascade) .IsRequired(); }); #pragma warning restore 612, 618 } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Migrations/ConfigurationDb.sql ================================================ IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL BEGIN CREATE TABLE [__EFMigrationsHistory] ( [MigrationId] nvarchar(150) NOT NULL, [ProductVersion] nvarchar(32) NOT NULL, CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId]) ); END; GO CREATE TABLE [ApiResources] ( [Id] int NOT NULL IDENTITY, [Enabled] bit NOT NULL, [Name] nvarchar(200) NOT NULL, [DisplayName] nvarchar(200) NULL, [Description] nvarchar(1000) NULL, [AllowedAccessTokenSigningAlgorithms] nvarchar(100) NULL, [ShowInDiscoveryDocument] bit NOT NULL, [Created] datetime2 NOT NULL, [Updated] datetime2 NULL, [LastAccessed] datetime2 NULL, [NonEditable] bit NOT NULL, CONSTRAINT [PK_ApiResources] PRIMARY KEY ([Id]) ); GO CREATE TABLE [ApiScopes] ( [Id] int NOT NULL IDENTITY, [Enabled] bit NOT NULL, [Name] nvarchar(200) NOT NULL, [DisplayName] nvarchar(200) NULL, [Description] nvarchar(1000) NULL, [Required] bit NOT NULL, [Emphasize] bit NOT NULL, [ShowInDiscoveryDocument] bit NOT NULL, CONSTRAINT [PK_ApiScopes] PRIMARY KEY ([Id]) ); GO CREATE TABLE [Clients] ( [Id] int NOT NULL IDENTITY, [Enabled] bit NOT NULL, [ClientId] nvarchar(200) NOT NULL, [ProtocolType] nvarchar(200) NOT NULL, [RequireClientSecret] bit NOT NULL, [ClientName] nvarchar(200) NULL, [Description] nvarchar(1000) NULL, [ClientUri] nvarchar(2000) NULL, [LogoUri] nvarchar(2000) NULL, [RequireConsent] bit NOT NULL, [AllowRememberConsent] bit NOT NULL, [AlwaysIncludeUserClaimsInIdToken] bit NOT NULL, [RequirePkce] bit NOT NULL, [AllowPlainTextPkce] bit NOT NULL, [RequireRequestObject] bit NOT NULL, [AllowAccessTokensViaBrowser] bit NOT NULL, [FrontChannelLogoutUri] nvarchar(2000) NULL, [FrontChannelLogoutSessionRequired] bit NOT NULL, [BackChannelLogoutUri] nvarchar(2000) NULL, [BackChannelLogoutSessionRequired] bit NOT NULL, [AllowOfflineAccess] bit NOT NULL, [IdentityTokenLifetime] int NOT NULL, [AllowedIdentityTokenSigningAlgorithms] nvarchar(100) NULL, [AccessTokenLifetime] int NOT NULL, [AuthorizationCodeLifetime] int NOT NULL, [ConsentLifetime] int NULL, [AbsoluteRefreshTokenLifetime] int NOT NULL, [SlidingRefreshTokenLifetime] int NOT NULL, [RefreshTokenUsage] int NOT NULL, [UpdateAccessTokenClaimsOnRefresh] bit NOT NULL, [RefreshTokenExpiration] int NOT NULL, [AccessTokenType] int NOT NULL, [EnableLocalLogin] bit NOT NULL, [IncludeJwtId] bit NOT NULL, [AlwaysSendClientClaims] bit NOT NULL, [ClientClaimsPrefix] nvarchar(200) NULL, [PairWiseSubjectSalt] nvarchar(200) NULL, [Created] datetime2 NOT NULL, [Updated] datetime2 NULL, [LastAccessed] datetime2 NULL, [UserSsoLifetime] int NULL, [UserCodeType] nvarchar(100) NULL, [DeviceCodeLifetime] int NOT NULL, [NonEditable] bit NOT NULL, CONSTRAINT [PK_Clients] PRIMARY KEY ([Id]) ); GO CREATE TABLE [IdentityResources] ( [Id] int NOT NULL IDENTITY, [Enabled] bit NOT NULL, [Name] nvarchar(200) NOT NULL, [DisplayName] nvarchar(200) NULL, [Description] nvarchar(1000) NULL, [Required] bit NOT NULL, [Emphasize] bit NOT NULL, [ShowInDiscoveryDocument] bit NOT NULL, [Created] datetime2 NOT NULL, [Updated] datetime2 NULL, [NonEditable] bit NOT NULL, CONSTRAINT [PK_IdentityResources] PRIMARY KEY ([Id]) ); GO CREATE TABLE [ApiResourceClaims] ( [Id] int NOT NULL IDENTITY, [Type] nvarchar(200) NOT NULL, [ApiResourceId] int NOT NULL, CONSTRAINT [PK_ApiResourceClaims] PRIMARY KEY ([Id]), CONSTRAINT [FK_ApiResourceClaims_ApiResources_ApiResourceId] FOREIGN KEY ([ApiResourceId]) REFERENCES [ApiResources] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ApiResourceProperties] ( [Id] int NOT NULL IDENTITY, [Key] nvarchar(250) NOT NULL, [Value] nvarchar(2000) NOT NULL, [ApiResourceId] int NOT NULL, CONSTRAINT [PK_ApiResourceProperties] PRIMARY KEY ([Id]), CONSTRAINT [FK_ApiResourceProperties_ApiResources_ApiResourceId] FOREIGN KEY ([ApiResourceId]) REFERENCES [ApiResources] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ApiResourceScopes] ( [Id] int NOT NULL IDENTITY, [Scope] nvarchar(200) NOT NULL, [ApiResourceId] int NOT NULL, CONSTRAINT [PK_ApiResourceScopes] PRIMARY KEY ([Id]), CONSTRAINT [FK_ApiResourceScopes_ApiResources_ApiResourceId] FOREIGN KEY ([ApiResourceId]) REFERENCES [ApiResources] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ApiResourceSecrets] ( [Id] int NOT NULL IDENTITY, [Description] nvarchar(1000) NULL, [Value] nvarchar(4000) NOT NULL, [Expiration] datetime2 NULL, [Type] nvarchar(250) NOT NULL, [Created] datetime2 NOT NULL, [ApiResourceId] int NOT NULL, CONSTRAINT [PK_ApiResourceSecrets] PRIMARY KEY ([Id]), CONSTRAINT [FK_ApiResourceSecrets_ApiResources_ApiResourceId] FOREIGN KEY ([ApiResourceId]) REFERENCES [ApiResources] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ApiScopeClaims] ( [Id] int NOT NULL IDENTITY, [Type] nvarchar(200) NOT NULL, [ScopeId] int NOT NULL, CONSTRAINT [PK_ApiScopeClaims] PRIMARY KEY ([Id]), CONSTRAINT [FK_ApiScopeClaims_ApiScopes_ScopeId] FOREIGN KEY ([ScopeId]) REFERENCES [ApiScopes] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ApiScopeProperties] ( [Id] int NOT NULL IDENTITY, [Key] nvarchar(250) NOT NULL, [Value] nvarchar(2000) NOT NULL, [ScopeId] int NOT NULL, CONSTRAINT [PK_ApiScopeProperties] PRIMARY KEY ([Id]), CONSTRAINT [FK_ApiScopeProperties_ApiScopes_ScopeId] FOREIGN KEY ([ScopeId]) REFERENCES [ApiScopes] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientClaims] ( [Id] int NOT NULL IDENTITY, [Type] nvarchar(250) NOT NULL, [Value] nvarchar(250) NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientClaims] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientClaims_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientCorsOrigins] ( [Id] int NOT NULL IDENTITY, [Origin] nvarchar(150) NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientCorsOrigins] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientCorsOrigins_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientGrantTypes] ( [Id] int NOT NULL IDENTITY, [GrantType] nvarchar(250) NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientGrantTypes] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientGrantTypes_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientIdPRestrictions] ( [Id] int NOT NULL IDENTITY, [Provider] nvarchar(200) NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientIdPRestrictions] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientIdPRestrictions_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientPostLogoutRedirectUris] ( [Id] int NOT NULL IDENTITY, [PostLogoutRedirectUri] nvarchar(2000) NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientPostLogoutRedirectUris] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientPostLogoutRedirectUris_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientProperties] ( [Id] int NOT NULL IDENTITY, [Key] nvarchar(250) NOT NULL, [Value] nvarchar(2000) NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientProperties] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientProperties_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientRedirectUris] ( [Id] int NOT NULL IDENTITY, [RedirectUri] nvarchar(2000) NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientRedirectUris] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientRedirectUris_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientScopes] ( [Id] int NOT NULL IDENTITY, [Scope] nvarchar(200) NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientScopes] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientScopes_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [ClientSecrets] ( [Id] int NOT NULL IDENTITY, [Description] nvarchar(2000) NULL, [Value] nvarchar(4000) NOT NULL, [Expiration] datetime2 NULL, [Type] nvarchar(250) NOT NULL, [Created] datetime2 NOT NULL, [ClientId] int NOT NULL, CONSTRAINT [PK_ClientSecrets] PRIMARY KEY ([Id]), CONSTRAINT [FK_ClientSecrets_Clients_ClientId] FOREIGN KEY ([ClientId]) REFERENCES [Clients] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [IdentityResourceClaims] ( [Id] int NOT NULL IDENTITY, [Type] nvarchar(200) NOT NULL, [IdentityResourceId] int NOT NULL, CONSTRAINT [PK_IdentityResourceClaims] PRIMARY KEY ([Id]), CONSTRAINT [FK_IdentityResourceClaims_IdentityResources_IdentityResourceId] FOREIGN KEY ([IdentityResourceId]) REFERENCES [IdentityResources] ([Id]) ON DELETE CASCADE ); GO CREATE TABLE [IdentityResourceProperties] ( [Id] int NOT NULL IDENTITY, [Key] nvarchar(250) NOT NULL, [Value] nvarchar(2000) NOT NULL, [IdentityResourceId] int NOT NULL, CONSTRAINT [PK_IdentityResourceProperties] PRIMARY KEY ([Id]), CONSTRAINT [FK_IdentityResourceProperties_IdentityResources_IdentityResourceId] FOREIGN KEY ([IdentityResourceId]) REFERENCES [IdentityResources] ([Id]) ON DELETE CASCADE ); GO CREATE INDEX [IX_ApiResourceClaims_ApiResourceId] ON [ApiResourceClaims] ([ApiResourceId]); GO CREATE INDEX [IX_ApiResourceProperties_ApiResourceId] ON [ApiResourceProperties] ([ApiResourceId]); GO CREATE UNIQUE INDEX [IX_ApiResources_Name] ON [ApiResources] ([Name]); GO CREATE INDEX [IX_ApiResourceScopes_ApiResourceId] ON [ApiResourceScopes] ([ApiResourceId]); GO CREATE INDEX [IX_ApiResourceSecrets_ApiResourceId] ON [ApiResourceSecrets] ([ApiResourceId]); GO CREATE INDEX [IX_ApiScopeClaims_ScopeId] ON [ApiScopeClaims] ([ScopeId]); GO CREATE INDEX [IX_ApiScopeProperties_ScopeId] ON [ApiScopeProperties] ([ScopeId]); GO CREATE UNIQUE INDEX [IX_ApiScopes_Name] ON [ApiScopes] ([Name]); GO CREATE INDEX [IX_ClientClaims_ClientId] ON [ClientClaims] ([ClientId]); GO CREATE INDEX [IX_ClientCorsOrigins_ClientId] ON [ClientCorsOrigins] ([ClientId]); GO CREATE INDEX [IX_ClientGrantTypes_ClientId] ON [ClientGrantTypes] ([ClientId]); GO CREATE INDEX [IX_ClientIdPRestrictions_ClientId] ON [ClientIdPRestrictions] ([ClientId]); GO CREATE INDEX [IX_ClientPostLogoutRedirectUris_ClientId] ON [ClientPostLogoutRedirectUris] ([ClientId]); GO CREATE INDEX [IX_ClientProperties_ClientId] ON [ClientProperties] ([ClientId]); GO CREATE INDEX [IX_ClientRedirectUris_ClientId] ON [ClientRedirectUris] ([ClientId]); GO CREATE UNIQUE INDEX [IX_Clients_ClientId] ON [Clients] ([ClientId]); GO CREATE INDEX [IX_ClientScopes_ClientId] ON [ClientScopes] ([ClientId]); GO CREATE INDEX [IX_ClientSecrets_ClientId] ON [ClientSecrets] ([ClientId]); GO CREATE INDEX [IX_IdentityResourceClaims_IdentityResourceId] ON [IdentityResourceClaims] ([IdentityResourceId]); GO CREATE INDEX [IX_IdentityResourceProperties_IdentityResourceId] ON [IdentityResourceProperties] ([IdentityResourceId]); GO CREATE UNIQUE INDEX [IX_IdentityResources_Name] ON [IdentityResources] ([Name]); GO INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) VALUES (N'20200522172542_Config', N'3.1.0'); GO ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Migrations/PersistedGrantDb/20200522172538_Grants.Designer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // namespace SqlServer.Migrations.PersistedGrantDb { [DbContext(typeof(PersistedGrantDbContext))] [Migration("20200522172538_Grants")] partial class Grants { protected override void BuildTargetModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.0") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() .HasColumnType("nvarchar(max)") .HasMaxLength(50000); b.Property("Description") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("DeviceCode") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); b.Property("SessionId") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("SubjectId") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("UserCode"); b.HasIndex("DeviceCode") .IsUnique(); b.HasIndex("Expiration"); b.ToTable("DeviceCodes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ConsumedTime") .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() .HasColumnType("nvarchar(max)") .HasMaxLength(50000); b.Property("Description") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("SessionId") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("SubjectId") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(50)") .HasMaxLength(50); b.HasKey("Key"); b.HasIndex("Expiration"); b.HasIndex("SubjectId", "ClientId", "Type"); b.HasIndex("SubjectId", "SessionId", "Type"); b.ToTable("PersistedGrants"); }); #pragma warning restore 612, 618 } } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Migrations/PersistedGrantDb/20200522172538_Grants.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace SqlServer.Migrations.PersistedGrantDb; public partial class Grants : Migration { protected override void Up(MigrationBuilder migrationBuilder) { migrationBuilder.CreateTable( name: "DeviceCodes", columns: table => new { UserCode = table.Column(maxLength: 200, nullable: false), DeviceCode = table.Column(maxLength: 200, nullable: false), SubjectId = table.Column(maxLength: 200, nullable: true), SessionId = table.Column(maxLength: 100, nullable: true), ClientId = table.Column(maxLength: 200, nullable: false), Description = table.Column(maxLength: 200, nullable: true), CreationTime = table.Column(nullable: false), Expiration = table.Column(nullable: false), Data = table.Column(maxLength: 50000, nullable: false) }, constraints: table => { table.PrimaryKey("PK_DeviceCodes", x => x.UserCode); }); migrationBuilder.CreateTable( name: "PersistedGrants", columns: table => new { Key = table.Column(maxLength: 200, nullable: false), Type = table.Column(maxLength: 50, nullable: false), SubjectId = table.Column(maxLength: 200, nullable: true), SessionId = table.Column(maxLength: 100, nullable: true), ClientId = table.Column(maxLength: 200, nullable: false), Description = table.Column(maxLength: 200, nullable: true), CreationTime = table.Column(nullable: false), Expiration = table.Column(nullable: true), ConsumedTime = table.Column(nullable: true), Data = table.Column(maxLength: 50000, nullable: false) }, constraints: table => { table.PrimaryKey("PK_PersistedGrants", x => x.Key); }); migrationBuilder.CreateIndex( name: "IX_DeviceCodes_DeviceCode", table: "DeviceCodes", column: "DeviceCode", unique: true); migrationBuilder.CreateIndex( name: "IX_DeviceCodes_Expiration", table: "DeviceCodes", column: "Expiration"); migrationBuilder.CreateIndex( name: "IX_PersistedGrants_Expiration", table: "PersistedGrants", column: "Expiration"); migrationBuilder.CreateIndex( name: "IX_PersistedGrants_SubjectId_ClientId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "ClientId", "Type" }); migrationBuilder.CreateIndex( name: "IX_PersistedGrants_SubjectId_SessionId_Type", table: "PersistedGrants", columns: new[] { "SubjectId", "SessionId", "Type" }); } protected override void Down(MigrationBuilder migrationBuilder) { migrationBuilder.DropTable( name: "DeviceCodes"); migrationBuilder.DropTable( name: "PersistedGrants"); } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Migrations/PersistedGrantDb/PersistedGrantDbContextModelSnapshot.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ // namespace SqlServer.Migrations.PersistedGrantDb; [DbContext(typeof(PersistedGrantDbContext))] partial class PersistedGrantDbContextModelSnapshot : ModelSnapshot { protected override void BuildModel(ModelBuilder modelBuilder) { #pragma warning disable 612, 618 modelBuilder .HasAnnotation("ProductVersion", "3.1.0") .HasAnnotation("Relational:MaxIdentifierLength", 128) .HasAnnotation("SqlServer:ValueGenerationStrategy", SqlServerValueGenerationStrategy.IdentityColumn); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.DeviceFlowCodes", b => { b.Property("UserCode") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() .HasColumnType("nvarchar(max)") .HasMaxLength(50000); b.Property("Description") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("DeviceCode") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Expiration") .IsRequired() .HasColumnType("datetime2"); b.Property("SessionId") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("SubjectId") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.HasKey("UserCode"); b.HasIndex("DeviceCode") .IsUnique(); b.HasIndex("Expiration"); b.ToTable("DeviceCodes"); }); modelBuilder.Entity("IdentityServer8.EntityFramework.Entities.PersistedGrant", b => { b.Property("Key") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ClientId") .IsRequired() .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("ConsumedTime") .HasColumnType("datetime2"); b.Property("CreationTime") .HasColumnType("datetime2"); b.Property("Data") .IsRequired() .HasColumnType("nvarchar(max)") .HasMaxLength(50000); b.Property("Description") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Expiration") .HasColumnType("datetime2"); b.Property("SessionId") .HasColumnType("nvarchar(100)") .HasMaxLength(100); b.Property("SubjectId") .HasColumnType("nvarchar(200)") .HasMaxLength(200); b.Property("Type") .IsRequired() .HasColumnType("nvarchar(50)") .HasMaxLength(50); b.HasKey("Key"); b.HasIndex("Expiration"); b.HasIndex("SubjectId", "ClientId", "Type"); b.HasIndex("SubjectId", "SessionId", "Type"); b.ToTable("PersistedGrants"); }); #pragma warning restore 612, 618 } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Migrations/PersistedGrantDb.sql ================================================ IF OBJECT_ID(N'[__EFMigrationsHistory]') IS NULL BEGIN CREATE TABLE [__EFMigrationsHistory] ( [MigrationId] nvarchar(150) NOT NULL, [ProductVersion] nvarchar(32) NOT NULL, CONSTRAINT [PK___EFMigrationsHistory] PRIMARY KEY ([MigrationId]) ); END; GO CREATE TABLE [DeviceCodes] ( [UserCode] nvarchar(200) NOT NULL, [DeviceCode] nvarchar(200) NOT NULL, [SubjectId] nvarchar(200) NULL, [SessionId] nvarchar(100) NULL, [ClientId] nvarchar(200) NOT NULL, [Description] nvarchar(200) NULL, [CreationTime] datetime2 NOT NULL, [Expiration] datetime2 NOT NULL, [Data] nvarchar(max) NOT NULL, CONSTRAINT [PK_DeviceCodes] PRIMARY KEY ([UserCode]) ); GO CREATE TABLE [PersistedGrants] ( [Key] nvarchar(200) NOT NULL, [Type] nvarchar(50) NOT NULL, [SubjectId] nvarchar(200) NULL, [SessionId] nvarchar(100) NULL, [ClientId] nvarchar(200) NOT NULL, [Description] nvarchar(200) NULL, [CreationTime] datetime2 NOT NULL, [Expiration] datetime2 NULL, [ConsumedTime] datetime2 NULL, [Data] nvarchar(max) NOT NULL, CONSTRAINT [PK_PersistedGrants] PRIMARY KEY ([Key]) ); GO CREATE UNIQUE INDEX [IX_DeviceCodes_DeviceCode] ON [DeviceCodes] ([DeviceCode]); GO CREATE INDEX [IX_DeviceCodes_Expiration] ON [DeviceCodes] ([Expiration]); GO CREATE INDEX [IX_PersistedGrants_Expiration] ON [PersistedGrants] ([Expiration]); GO CREATE INDEX [IX_PersistedGrants_SubjectId_ClientId_Type] ON [PersistedGrants] ([SubjectId], [ClientId], [Type]); GO CREATE INDEX [IX_PersistedGrants_SubjectId_SessionId_Type] ON [PersistedGrants] ([SubjectId], [SessionId], [Type]); GO INSERT INTO [__EFMigrationsHistory] ([MigrationId], [ProductVersion]) VALUES (N'20200522172538_Grants', N'3.1.0'); GO ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace SqlServer; class Program { public static void Main(string[] args) { } public static IWebHost BuildWebHost(string[] args) => WebHost.CreateDefaultBuilder(args) .UseStartup() .Build(); } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Properties/launchSettings.json ================================================ { "iisSettings": { "windowsAuthentication": false, "anonymousAuthentication": true, "iisExpress": { "applicationUrl": "http://localhost:7603/", "sslPort": 0 } }, "profiles": { "IIS Express": { "commandName": "IISExpress", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" } }, "SqlServer": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "http://localhost:7604/" } } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/SqlServer.csproj ================================================ ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace SqlServer; public class Startup { public IConfiguration Configuration { get; } public Startup(IConfiguration config) { Configuration = config; } public void ConfigureServices(IServiceCollection services) { var cn = Configuration.GetConnectionString("db"); services.AddOperationalDbContext(options => { options.ConfigureDbContext = b => b.UseSqlServer(cn, dbOpts => dbOpts.MigrationsAssembly(typeof(Startup).Assembly.FullName)); }); services.AddConfigurationDbContext(options => { options.ConfigureDbContext = b => b.UseSqlServer(cn, dbOpts => dbOpts.MigrationsAssembly(typeof(Startup).Assembly.FullName)); }); } public void Configure(IApplicationBuilder app) { } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/appsettings.json ================================================ { "ConnectionStrings": { "db": "server=(localdb)\\mssqllocaldb;database=IdentityServer8.EntityFramework-8.0.0;trusted_connection=yes;", //"db": "Data Source=IdentityServer.db;" } } ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/buildschema.bat ================================================ rmdir /S /Q Migrations dotnet ef migrations add Grants -c PersistedGrantDbContext -o Migrations/PersistedGrantDb dotnet ef migrations add Configuration -c ConfigurationDbContext -o Migrations/ConfigurationDb dotnet ef migrations script -c PersistedGrantDbContext -o Migrations/PersistedGrantDb.sql dotnet ef migrations script -c ConfigurationDbContext -o Migrations/ConfigurationDb.sql ================================================ FILE: src/EntityFramework.Storage/migrations/SqlServer/createdb.bat ================================================ dotnet ef database update -c PersistedGrantDbContext dotnet ef database update -c ConfigurationDbContext ================================================ FILE: src/EntityFramework.Storage/src/Configuration/ServiceCollectionExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Storage { /// /// Extension methods to add EF database support to IdentityServer. /// public static class IdentityServerEntityFrameworkBuilderExtensions { /// /// Add Configuration DbContext to the DI system. /// /// /// The store options action. /// public static IServiceCollection AddConfigurationDbContext(this IServiceCollection services, Action storeOptionsAction = null) { return services.AddConfigurationDbContext(storeOptionsAction); } /// /// Add Configuration DbContext to the DI system. /// /// The IConfigurationDbContext to use. /// /// The store options action. /// public static IServiceCollection AddConfigurationDbContext(this IServiceCollection services, Action storeOptionsAction = null) where TContext : DbContext, IConfigurationDbContext { var options = new ConfigurationStoreOptions(); services.AddSingleton(options); storeOptionsAction?.Invoke(options); if (options.ResolveDbContextOptions != null) { services.AddDbContext(options.ResolveDbContextOptions); } else { services.AddDbContext(dbCtxBuilder => { options.ConfigureDbContext?.Invoke(dbCtxBuilder); }); } services.AddScoped(); return services; } /// /// Adds operational DbContext to the DI system. /// /// /// The store options action. /// public static IServiceCollection AddOperationalDbContext(this IServiceCollection services, Action storeOptionsAction = null) { return services.AddOperationalDbContext(storeOptionsAction); } /// /// Adds operational DbContext to the DI system. /// /// The IPersistedGrantDbContext to use. /// /// The store options action. /// public static IServiceCollection AddOperationalDbContext(this IServiceCollection services, Action storeOptionsAction = null) where TContext : DbContext, IPersistedGrantDbContext { var storeOptions = new OperationalStoreOptions(); services.AddSingleton(storeOptions); storeOptionsAction?.Invoke(storeOptions); if (storeOptions.ResolveDbContextOptions != null) { services.AddDbContext(storeOptions.ResolveDbContextOptions); } else { services.AddDbContext(dbCtxBuilder => { storeOptions.ConfigureDbContext?.Invoke(dbCtxBuilder); }); } services.AddScoped(); services.AddTransient(); return services; } /// /// Adds an implementation of the IOperationalStoreNotification to the DI system. /// /// /// /// public static IServiceCollection AddOperationalStoreNotification(this IServiceCollection services) where T : class, IOperationalStoreNotification { services.AddTransient(); return services; } } } ================================================ FILE: src/EntityFramework.Storage/src/DbContexts/ConfigurationDbContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework.DbContexts { /// /// DbContext for the IdentityServer configuration data. /// /// /// public class ConfigurationDbContext : ConfigurationDbContext { /// /// Initializes a new instance of the class. /// /// The options. /// The store options. /// storeOptions public ConfigurationDbContext(DbContextOptions options, ConfigurationStoreOptions storeOptions) : base(options, storeOptions) { } } /// /// DbContext for the IdentityServer configuration data. /// /// /// public class ConfigurationDbContext : DbContext, IConfigurationDbContext where TContext : DbContext, IConfigurationDbContext { private readonly ConfigurationStoreOptions storeOptions; /// /// Initializes a new instance of the class. /// /// The options. /// The store options. /// storeOptions public ConfigurationDbContext(DbContextOptions options, ConfigurationStoreOptions storeOptions) : base(options) { this.storeOptions = storeOptions ?? throw new ArgumentNullException(nameof(storeOptions)); } /// /// Gets or sets the clients. /// /// /// The clients. /// public DbSet Clients { get; set; } /// /// Gets or sets the clients' CORS origins. /// /// /// The clients CORS origins. /// public DbSet ClientCorsOrigins { get; set; } /// /// Gets or sets the identity resources. /// /// /// The identity resources. /// public DbSet IdentityResources { get; set; } /// /// Gets or sets the API resources. /// /// /// The API resources. /// public DbSet ApiResources { get; set; } /// /// Gets or sets the API scopes. /// /// /// The API resources. /// public DbSet ApiScopes { get; set; } /// /// Override this method to further configure the model that was discovered by convention from the entity types /// exposed in properties on your derived context. The resulting model may be cached /// and re-used for subsequent instances of your derived context. /// /// The builder being used to construct the model for this context. Databases (and other extensions) typically /// define extension methods on this object that allow you to configure aspects of the model that are specific /// to a given database. /// /// If a model is explicitly set on the options for this context (via ) /// then this method will not be run. /// protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ConfigureClientContext(storeOptions); modelBuilder.ConfigureResourcesContext(storeOptions); base.OnModelCreating(modelBuilder); } } } ================================================ FILE: src/EntityFramework.Storage/src/DbContexts/PersistedGrantDbContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework.DbContexts { /// /// DbContext for the IdentityServer operational data. /// /// /// public class PersistedGrantDbContext : PersistedGrantDbContext { /// /// Initializes a new instance of the class. /// /// The options. /// The store options. /// storeOptions public PersistedGrantDbContext(DbContextOptions options, OperationalStoreOptions storeOptions) : base(options, storeOptions) { } } /// /// DbContext for the IdentityServer operational data. /// /// /// public class PersistedGrantDbContext : DbContext, IPersistedGrantDbContext where TContext : DbContext, IPersistedGrantDbContext { private readonly OperationalStoreOptions storeOptions; /// /// Initializes a new instance of the class. /// /// The options. /// The store options. /// storeOptions public PersistedGrantDbContext(DbContextOptions options, OperationalStoreOptions storeOptions) : base(options) { if (storeOptions == null) throw new ArgumentNullException(nameof(storeOptions)); this.storeOptions = storeOptions; } /// /// Gets or sets the persisted grants. /// /// /// The persisted grants. /// public DbSet PersistedGrants { get; set; } /// /// Gets or sets the device codes. /// /// /// The device codes. /// public DbSet DeviceFlowCodes { get; set; } /// /// Saves the changes. /// /// public virtual Task SaveChangesAsync() { return base.SaveChangesAsync(); } /// /// Override this method to further configure the model that was discovered by convention from the entity types /// exposed in properties on your derived context. The resulting model may be cached /// and re-used for subsequent instances of your derived context. /// /// The builder being used to construct the model for this context. Databases (and other extensions) typically /// define extension methods on this object that allow you to configure aspects of the model that are specific /// to a given database. /// /// If a model is explicitly set on the options for this context (via ) /// then this method will not be run. /// protected override void OnModelCreating(ModelBuilder modelBuilder) { modelBuilder.ConfigurePersistedGrantContext(storeOptions); base.OnModelCreating(modelBuilder); } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ApiResource.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ApiResource { public int Id { get; set; } public bool Enabled { get; set; } = true; public string Name { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public string AllowedAccessTokenSigningAlgorithms { get; set; } public bool ShowInDiscoveryDocument { get; set; } = true; public List Secrets { get; set; } public List Scopes { get; set; } public List UserClaims { get; set; } public List Properties { get; set; } public DateTime Created { get; set; } = DateTime.UtcNow; public DateTime? Updated { get; set; } public DateTime? LastAccessed { get; set; } public bool NonEditable { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ApiResourceClaim.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ApiResourceClaim : UserClaim { public int ApiResourceId { get; set; } public ApiResource ApiResource { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ApiResourceProperty.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ApiResourceProperty : Property { public int ApiResourceId { get; set; } public ApiResource ApiResource { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ApiResourceScope.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ApiResourceScope { public int Id { get; set; } public string Scope { get; set; } public int ApiResourceId { get; set; } public ApiResource ApiResource { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ApiResourceSecret.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ApiResourceSecret : Secret { public int ApiResourceId { get; set; } public ApiResource ApiResource { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ApiScope.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 using System.Collections.Generic; namespace IdentityServer8.EntityFramework.Entities { public class ApiScope { public int Id { get; set; } public bool Enabled { get; set; } = true; public string Name { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Required { get; set; } public bool Emphasize { get; set; } public bool ShowInDiscoveryDocument { get; set; } = true; public List UserClaims { get; set; } public List Properties { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ApiScopeClaim.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ApiScopeClaim : UserClaim { public int ScopeId { get; set; } public ApiScope Scope { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ApiScopeProperty.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ApiScopeProperty : Property { public int ScopeId { get; set; } public ApiScope Scope { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/Client.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 using IdentityServer8.Models; namespace IdentityServer8.EntityFramework.Entities { public class Client { public int Id { get; set; } public bool Enabled { get; set; } = true; public string ClientId { get; set; } public string ProtocolType { get; set; } = "oidc"; public List ClientSecrets { get; set; } public bool RequireClientSecret { get; set; } = true; public string ClientName { get; set; } public string Description { get; set; } public string ClientUri { get; set; } public string LogoUri { get; set; } public bool RequireConsent { get; set; } = false; public bool AllowRememberConsent { get; set; } = true; public bool AlwaysIncludeUserClaimsInIdToken { get; set; } public List AllowedGrantTypes { get; set; } public bool RequirePkce { get; set; } = true; public bool AllowPlainTextPkce { get; set; } public bool RequireRequestObject { get; set; } public bool AllowAccessTokensViaBrowser { get; set; } public List RedirectUris { get; set; } public List PostLogoutRedirectUris { get; set; } public string FrontChannelLogoutUri { get; set; } public bool FrontChannelLogoutSessionRequired { get; set; } = true; public string BackChannelLogoutUri { get; set; } public bool BackChannelLogoutSessionRequired { get; set; } = true; public bool AllowOfflineAccess { get; set; } public List AllowedScopes { get; set; } public int IdentityTokenLifetime { get; set; } = 300; public string AllowedIdentityTokenSigningAlgorithms { get; set; } public int AccessTokenLifetime { get; set; } = 3600; public int AuthorizationCodeLifetime { get; set; } = 300; public int? ConsentLifetime { get; set; } = null; public int AbsoluteRefreshTokenLifetime { get; set; } = 2592000; public int SlidingRefreshTokenLifetime { get; set; } = 1296000; public int RefreshTokenUsage { get; set; } = (int)TokenUsage.OneTimeOnly; public bool UpdateAccessTokenClaimsOnRefresh { get; set; } public int RefreshTokenExpiration { get; set; } = (int)TokenExpiration.Absolute; public int AccessTokenType { get; set; } = (int)0; // AccessTokenType.Jwt; public bool EnableLocalLogin { get; set; } = true; public List IdentityProviderRestrictions { get; set; } public bool IncludeJwtId { get; set; } public List Claims { get; set; } public bool AlwaysSendClientClaims { get; set; } public string ClientClaimsPrefix { get; set; } = "client_"; public string PairWiseSubjectSalt { get; set; } public List AllowedCorsOrigins { get; set; } public List Properties { get; set; } public DateTime Created { get; set; } = DateTime.UtcNow; public DateTime? Updated { get; set; } public DateTime? LastAccessed { get; set; } public int? UserSsoLifetime { get; set; } public string UserCodeType { get; set; } public int DeviceCodeLifetime { get; set; } = 300; public bool NonEditable { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientClaim.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientClaim { public int Id { get; set; } public string Type { get; set; } public string Value { get; set; } public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientCorsOrigin.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientCorsOrigin { public int Id { get; set; } public string Origin { get; set; } public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientGrantType.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientGrantType { public int Id { get; set; } public string GrantType { get; set; } public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientIdPRestriction.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientIdPRestriction { public int Id { get; set; } public string Provider { get; set; } public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientPostLogoutRedirectUri.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientPostLogoutRedirectUri { public int Id { get; set; } public string PostLogoutRedirectUri { get; set; } public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientProperty.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientProperty : Property { public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientRedirectUri.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientRedirectUri { public int Id { get; set; } public string RedirectUri { get; set; } public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientScope.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientScope { public int Id { get; set; } public string Scope { get; set; } public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/ClientSecret.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class ClientSecret : Secret { public int ClientId { get; set; } public Client Client { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/DeviceFlowCodes.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Entities { /// /// Entity for device flow codes /// public class DeviceFlowCodes { /// /// Gets or sets the device code. /// /// /// The device code. /// public string DeviceCode { get; set; } /// /// Gets or sets the user code. /// /// /// The user code. /// public string UserCode { get; set; } /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the session identifier. /// /// /// The session identifier. /// public string SessionId { get; set; } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } /// /// Gets or sets the creation time. /// /// /// The creation time. /// public DateTime CreationTime { get; set; } /// /// Gets or sets the expiration. /// /// /// The expiration. /// public DateTime? Expiration { get; set; } /// /// Gets or sets the data. /// /// /// The data. /// public string Data { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/IdentityResource.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class IdentityResource { public int Id { get; set; } public bool Enabled { get; set; } = true; public string Name { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Required { get; set; } public bool Emphasize { get; set; } public bool ShowInDiscoveryDocument { get; set; } = true; public List UserClaims { get; set; } public List Properties { get; set; } public DateTime Created { get; set; } = DateTime.UtcNow; public DateTime? Updated { get; set; } public bool NonEditable { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/IdentityResourceClaim.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class IdentityResourceClaim : UserClaim { public int IdentityResourceId { get; set; } public IdentityResource IdentityResource { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/IdentityResourceProperty.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class IdentityResourceProperty : Property { public int IdentityResourceId { get; set; } public IdentityResource IdentityResource { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/PersistedGrant.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public class PersistedGrant { public string Key { get; set; } public string Type { get; set; } public string SubjectId { get; set; } public string SessionId { get; set; } public string ClientId { get; set; } public string Description { get; set; } public DateTime CreationTime { get; set; } public DateTime? Expiration { get; set; } public DateTime? ConsumedTime { get; set; } public string Data { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/Property.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public abstract class Property { public int Id { get; set; } public string Key { get; set; } public string Value { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/Secret.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public abstract class Secret { public int Id { get; set; } public string Description { get; set; } public string Value { get; set; } public DateTime? Expiration { get; set; } public string Type { get; set; } = "SharedSecret"; public DateTime Created { get; set; } = DateTime.UtcNow; } } ================================================ FILE: src/EntityFramework.Storage/src/Entities/UserClaim.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.EntityFramework.Entities { public abstract class UserClaim { public int Id { get; set; } public string Type { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Extensions/ModelBuilderExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework.Extensions { /// /// Extension methods to define the database schema for the configuration and operational data stores. /// public static class ModelBuilderExtensions { private static EntityTypeBuilder ToTable(this EntityTypeBuilder entityTypeBuilder, TableConfiguration configuration) where TEntity : class { return string.IsNullOrWhiteSpace(configuration.Schema) ? entityTypeBuilder.ToTable(configuration.Name) : entityTypeBuilder.ToTable(configuration.Name, configuration.Schema); } /// /// Configures the client context. /// /// The model builder. /// The store options. public static void ConfigureClientContext(this ModelBuilder modelBuilder, ConfigurationStoreOptions storeOptions) { if (!string.IsNullOrWhiteSpace(storeOptions.DefaultSchema)) modelBuilder.HasDefaultSchema(storeOptions.DefaultSchema); modelBuilder.Entity(client => { client.ToTable(storeOptions.Client); client.HasKey(x => x.Id); client.Property(x => x.ClientId).HasMaxLength(200).IsRequired(); client.Property(x => x.ProtocolType).HasMaxLength(200).IsRequired(); client.Property(x => x.ClientName).HasMaxLength(200); client.Property(x => x.ClientUri).HasMaxLength(2000); client.Property(x => x.LogoUri).HasMaxLength(2000); client.Property(x => x.Description).HasMaxLength(1000); client.Property(x => x.FrontChannelLogoutUri).HasMaxLength(2000); client.Property(x => x.BackChannelLogoutUri).HasMaxLength(2000); client.Property(x => x.ClientClaimsPrefix).HasMaxLength(200); client.Property(x => x.PairWiseSubjectSalt).HasMaxLength(200); client.Property(x => x.UserCodeType).HasMaxLength(100); client.Property(x => x.AllowedIdentityTokenSigningAlgorithms).HasMaxLength(100); client.HasIndex(x => x.ClientId).IsUnique(); client.HasMany(x => x.AllowedGrantTypes).WithOne(x => x.Client).HasForeignKey(x=>x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); client.HasMany(x => x.RedirectUris).WithOne(x => x.Client).HasForeignKey(x => x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); client.HasMany(x => x.PostLogoutRedirectUris).WithOne(x => x.Client).HasForeignKey(x => x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); client.HasMany(x => x.AllowedScopes).WithOne(x => x.Client).HasForeignKey(x => x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); client.HasMany(x => x.ClientSecrets).WithOne(x => x.Client).HasForeignKey(x => x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); client.HasMany(x => x.Claims).WithOne(x => x.Client).HasForeignKey(x => x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); client.HasMany(x => x.IdentityProviderRestrictions).WithOne(x => x.Client).HasForeignKey(x => x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); client.HasMany(x => x.AllowedCorsOrigins).WithOne(x => x.Client).HasForeignKey(x => x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); client.HasMany(x => x.Properties).WithOne(x => x.Client).HasForeignKey(x => x.ClientId).IsRequired().OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(grantType => { grantType.ToTable(storeOptions.ClientGrantType); grantType.Property(x => x.GrantType).HasMaxLength(250).IsRequired(); }); modelBuilder.Entity(redirectUri => { redirectUri.ToTable(storeOptions.ClientRedirectUri); redirectUri.Property(x => x.RedirectUri).HasMaxLength(2000).IsRequired(); }); modelBuilder.Entity(postLogoutRedirectUri => { postLogoutRedirectUri.ToTable(storeOptions.ClientPostLogoutRedirectUri); postLogoutRedirectUri.Property(x => x.PostLogoutRedirectUri).HasMaxLength(2000).IsRequired(); }); modelBuilder.Entity(scope => { scope.ToTable(storeOptions.ClientScopes); scope.Property(x => x.Scope).HasMaxLength(200).IsRequired(); }); modelBuilder.Entity(secret => { secret.ToTable(storeOptions.ClientSecret); secret.Property(x => x.Value).HasMaxLength(4000).IsRequired(); secret.Property(x => x.Type).HasMaxLength(250).IsRequired(); secret.Property(x => x.Description).HasMaxLength(2000); }); modelBuilder.Entity(claim => { claim.ToTable(storeOptions.ClientClaim); claim.Property(x => x.Type).HasMaxLength(250).IsRequired(); claim.Property(x => x.Value).HasMaxLength(250).IsRequired(); }); modelBuilder.Entity(idPRestriction => { idPRestriction.ToTable(storeOptions.ClientIdPRestriction); idPRestriction.Property(x => x.Provider).HasMaxLength(200).IsRequired(); }); modelBuilder.Entity(corsOrigin => { corsOrigin.ToTable(storeOptions.ClientCorsOrigin); corsOrigin.Property(x => x.Origin).HasMaxLength(150).IsRequired(); }); modelBuilder.Entity(property => { property.ToTable(storeOptions.ClientProperty); property.Property(x => x.Key).HasMaxLength(250).IsRequired(); property.Property(x => x.Value).HasMaxLength(2000).IsRequired(); }); } /// /// Configures the persisted grant context. /// /// The model builder. /// The store options. public static void ConfigurePersistedGrantContext(this ModelBuilder modelBuilder, OperationalStoreOptions storeOptions) { if (!string.IsNullOrWhiteSpace(storeOptions.DefaultSchema)) modelBuilder.HasDefaultSchema(storeOptions.DefaultSchema); modelBuilder.Entity(grant => { grant.ToTable(storeOptions.PersistedGrants); grant.Property(x => x.Key).HasMaxLength(200).ValueGeneratedNever(); grant.Property(x => x.Type).HasMaxLength(50).IsRequired(); grant.Property(x => x.SubjectId).HasMaxLength(200); grant.Property(x => x.SessionId).HasMaxLength(100); grant.Property(x => x.ClientId).HasMaxLength(200).IsRequired(); grant.Property(x => x.Description).HasMaxLength(200); grant.Property(x => x.CreationTime).IsRequired(); // 50000 chosen to be explicit to allow enough size to avoid truncation, yet stay beneath the MySql row size limit of ~65K // apparently anything over 4K converts to nvarchar(max) on SqlServer grant.Property(x => x.Data).HasMaxLength(50000).IsRequired(); grant.HasKey(x => x.Key); grant.HasIndex(x => new { x.SubjectId, x.ClientId, x.Type }); grant.HasIndex(x => new { x.SubjectId, x.SessionId, x.Type }); grant.HasIndex(x => x.Expiration); }); modelBuilder.Entity(codes => { codes.ToTable(storeOptions.DeviceFlowCodes); codes.Property(x => x.DeviceCode).HasMaxLength(200).IsRequired(); codes.Property(x => x.UserCode).HasMaxLength(200).IsRequired(); codes.Property(x => x.SubjectId).HasMaxLength(200); codes.Property(x => x.SessionId).HasMaxLength(100); codes.Property(x => x.ClientId).HasMaxLength(200).IsRequired(); codes.Property(x => x.Description).HasMaxLength(200); codes.Property(x => x.CreationTime).IsRequired(); codes.Property(x => x.Expiration).IsRequired(); // 50000 chosen to be explicit to allow enough size to avoid truncation, yet stay beneath the MySql row size limit of ~65K // apparently anything over 4K converts to nvarchar(max) on SqlServer codes.Property(x => x.Data).HasMaxLength(50000).IsRequired(); codes.HasKey(x => new {x.UserCode}); codes.HasIndex(x => x.DeviceCode).IsUnique(); codes.HasIndex(x => x.Expiration); }); } /// /// Configures the resources context. /// /// The model builder. /// The store options. public static void ConfigureResourcesContext(this ModelBuilder modelBuilder, ConfigurationStoreOptions storeOptions) { if (!string.IsNullOrWhiteSpace(storeOptions.DefaultSchema)) modelBuilder.HasDefaultSchema(storeOptions.DefaultSchema); modelBuilder.Entity(identityResource => { identityResource.ToTable(storeOptions.IdentityResource).HasKey(x => x.Id); identityResource.Property(x => x.Name).HasMaxLength(200).IsRequired(); identityResource.Property(x => x.DisplayName).HasMaxLength(200); identityResource.Property(x => x.Description).HasMaxLength(1000); identityResource.HasIndex(x => x.Name).IsUnique(); identityResource.HasMany(x => x.UserClaims).WithOne(x => x.IdentityResource).HasForeignKey(x => x.IdentityResourceId).IsRequired().OnDelete(DeleteBehavior.Cascade); identityResource.HasMany(x => x.Properties).WithOne(x => x.IdentityResource).HasForeignKey(x => x.IdentityResourceId).IsRequired().OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(claim => { claim.ToTable(storeOptions.IdentityResourceClaim).HasKey(x => x.Id); claim.Property(x => x.Type).HasMaxLength(200).IsRequired(); }); modelBuilder.Entity(property => { property.ToTable(storeOptions.IdentityResourceProperty); property.Property(x => x.Key).HasMaxLength(250).IsRequired(); property.Property(x => x.Value).HasMaxLength(2000).IsRequired(); }); modelBuilder.Entity(apiResource => { apiResource.ToTable(storeOptions.ApiResource).HasKey(x => x.Id); apiResource.Property(x => x.Name).HasMaxLength(200).IsRequired(); apiResource.Property(x => x.DisplayName).HasMaxLength(200); apiResource.Property(x => x.Description).HasMaxLength(1000); apiResource.Property(x => x.AllowedAccessTokenSigningAlgorithms).HasMaxLength(100); apiResource.HasIndex(x => x.Name).IsUnique(); apiResource.HasMany(x => x.Secrets).WithOne(x => x.ApiResource).HasForeignKey(x => x.ApiResourceId).IsRequired().OnDelete(DeleteBehavior.Cascade); apiResource.HasMany(x => x.Scopes).WithOne(x => x.ApiResource).HasForeignKey(x => x.ApiResourceId).IsRequired().OnDelete(DeleteBehavior.Cascade); apiResource.HasMany(x => x.UserClaims).WithOne(x => x.ApiResource).HasForeignKey(x => x.ApiResourceId).IsRequired().OnDelete(DeleteBehavior.Cascade); apiResource.HasMany(x => x.Properties).WithOne(x => x.ApiResource).HasForeignKey(x => x.ApiResourceId).IsRequired().OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(apiSecret => { apiSecret.ToTable(storeOptions.ApiResourceSecret).HasKey(x => x.Id); apiSecret.Property(x => x.Description).HasMaxLength(1000); apiSecret.Property(x => x.Value).HasMaxLength(4000).IsRequired(); apiSecret.Property(x => x.Type).HasMaxLength(250).IsRequired(); }); modelBuilder.Entity(apiClaim => { apiClaim.ToTable(storeOptions.ApiResourceClaim).HasKey(x => x.Id); apiClaim.Property(x => x.Type).HasMaxLength(200).IsRequired(); }); modelBuilder.Entity((System.Action>)(apiScope => { apiScope.ToTable((TableConfiguration)storeOptions.ApiResourceScope).HasKey(x => x.Id); apiScope.Property(x => x.Scope).HasMaxLength(200).IsRequired(); })); modelBuilder.Entity(property => { property.ToTable(storeOptions.ApiResourceProperty); property.Property(x => x.Key).HasMaxLength(250).IsRequired(); property.Property(x => x.Value).HasMaxLength(2000).IsRequired(); }); modelBuilder.Entity(scope => { scope.ToTable(storeOptions.ApiScope).HasKey(x => x.Id); scope.Property(x => x.Name).HasMaxLength(200).IsRequired(); scope.Property(x => x.DisplayName).HasMaxLength(200); scope.Property(x => x.Description).HasMaxLength(1000); scope.HasIndex(x => x.Name).IsUnique(); scope.HasMany(x => x.UserClaims).WithOne(x => x.Scope).HasForeignKey(x => x.ScopeId).IsRequired().OnDelete(DeleteBehavior.Cascade); }); modelBuilder.Entity(scopeClaim => { scopeClaim.ToTable(storeOptions.ApiScopeClaim).HasKey(x => x.Id); scopeClaim.Property(x => x.Type).HasMaxLength(200).IsRequired(); }); modelBuilder.Entity(property => { property.ToTable(storeOptions.ApiScopeProperty).HasKey(x => x.Id); property.Property(x => x.Key).HasMaxLength(250).IsRequired(); property.Property(x => x.Value).HasMaxLength(2000).IsRequired(); }); } } } ================================================ FILE: src/EntityFramework.Storage/src/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using AutoMapper; global using IdentityModel; global using IdentityServer8.EntityFramework.DbContexts; global using IdentityServer8.EntityFramework.Extensions; global using IdentityServer8.EntityFramework.Interfaces; //global using IdentityServer8.EntityFramework.Entities; global using IdentityServer8.EntityFramework.Mappers; global using IdentityServer8.EntityFramework.Options; global using IdentityServer8.Extensions; //global using IdentityServer8.Models; global using IdentityServer8.Stores; global using IdentityServer8.Stores.Serialization; global using Microsoft.EntityFrameworkCore; global using Microsoft.EntityFrameworkCore.Metadata.Builders; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Logging; global using System.Buffers; global using System.Runtime.CompilerServices; global using ClaimValueTypes = System.Security.Claims.ClaimValueTypes; ================================================ FILE: src/EntityFramework.Storage/src/IdentityServer8.EntityFramework.Storage.csproj ================================================ EntityFramework persistence layer for IdentityServer8 true true True True ================================================ FILE: src/EntityFramework.Storage/src/Interfaces/IConfigurationDbContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework.Interfaces { /// /// Abstraction for the configuration context. /// /// public interface IConfigurationDbContext : IDisposable { /// /// Gets or sets the clients. /// /// /// The clients. /// DbSet Clients { get; set; } /// /// Gets or sets the clients' CORS origins. /// /// /// The clients CORS origins. /// DbSet ClientCorsOrigins { get; set; } /// /// Gets or sets the identity resources. /// /// /// The identity resources. /// DbSet IdentityResources { get; set; } /// /// Gets or sets the API resources. /// /// /// The API resources. /// DbSet ApiResources { get; set; } /// /// Gets or sets the scopes. /// /// /// The identity resources. /// DbSet ApiScopes { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Interfaces/IPersistedGrantDbContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework.Interfaces { /// /// Abstraction for the operational data context. /// /// public interface IPersistedGrantDbContext : IDisposable { /// /// Gets or sets the persisted grants. /// /// /// The persisted grants. /// DbSet PersistedGrants { get; set; } /// /// Gets or sets the device flow codes. /// /// /// The device flow codes. /// DbSet DeviceFlowCodes { get; set; } /// /// Saves the changes. /// /// Task SaveChangesAsync(); } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/AllowedSigningAlgorithmsConverter.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Mappers { class AllowedSigningAlgorithmsConverter : IValueConverter, string>, IValueConverter> { public static AllowedSigningAlgorithmsConverter Converter = new AllowedSigningAlgorithmsConverter(); public string Convert(ICollection sourceMember, ResolutionContext context) { if (sourceMember == null || !sourceMember.Any()) { return null; } return sourceMember.Aggregate((x, y) => $"{x},{y}"); } public ICollection Convert(string sourceMember, ResolutionContext context) { var list = new HashSet(); if (!String.IsNullOrWhiteSpace(sourceMember)) { sourceMember = sourceMember.Trim(); foreach (var item in sourceMember.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries).Distinct()) { list.Add(item); } } return list; } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/ApiResourceMapperProfile.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Mappers { /// /// Defines entity/model mapping for API resources. /// /// public class ApiResourceMapperProfile : Profile { /// /// /// public ApiResourceMapperProfile() { CreateMap>() .ReverseMap(); CreateMap(MemberList.Destination) .ConstructUsing(src => new Models.ApiResource()) .ForMember(x => x.ApiSecrets, opts => opts.MapFrom(x => x.Secrets)) .ForMember(x=>x.AllowedAccessTokenSigningAlgorithms, opts => opts.ConvertUsing(AllowedSigningAlgorithmsConverter.Converter, x=>x.AllowedAccessTokenSigningAlgorithms)) .ReverseMap() .ForMember(x => x.AllowedAccessTokenSigningAlgorithms, opts => opts.ConvertUsing(AllowedSigningAlgorithmsConverter.Converter, x => x.AllowedAccessTokenSigningAlgorithms)); CreateMap() .ConstructUsing(x => x.Type) .ReverseMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src)); CreateMap(MemberList.Destination) .ForMember(dest => dest.Type, opt => opt.Condition(srs => srs != null)) .ReverseMap(); CreateMap() .ConstructUsing(x => x.Scope) .ReverseMap() .ForMember(dest => dest.Scope, opt => opt.MapFrom(src => src)); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/ApiResourceMappers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework.Mappers { /// /// Extension methods to map to/from entity/model for API resources. /// public static class ApiResourceMappers { static ApiResourceMappers() { Mapper = new MapperConfiguration(cfg => cfg.AddProfile()) .CreateMapper(); } internal static IMapper Mapper { get; } /// /// Maps an entity to a model. /// /// The entity. /// public static Models.ApiResource ToModel(this ApiResource entity) { return entity == null ? null : Mapper.Map(entity); } /// /// Maps a model to an entity. /// /// The model. /// public static ApiResource ToEntity(this Models.ApiResource model) { return model == null ? null : Mapper.Map(model); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/ClientMapperProfile.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; namespace IdentityServer8.EntityFramework.Mappers { /// /// Defines entity/model mapping for clients. /// /// public class ClientMapperProfile : Profile { /// /// /// {ClientMapperProfile} /// /// public ClientMapperProfile() { CreateMap>() .ReverseMap(); CreateMap() .ForMember(dest => dest.ProtocolType, opt => opt.Condition(srs => srs != null)) .ForMember(x => x.AllowedIdentityTokenSigningAlgorithms, opts => opts.ConvertUsing(AllowedSigningAlgorithmsConverter.Converter, x => x.AllowedIdentityTokenSigningAlgorithms)) .ReverseMap() .ForMember(x => x.AllowedIdentityTokenSigningAlgorithms, opts => opts.ConvertUsing(AllowedSigningAlgorithmsConverter.Converter, x => x.AllowedIdentityTokenSigningAlgorithms)); CreateMap() .ConstructUsing(src => src.Origin) .ReverseMap() .ForMember(dest => dest.Origin, opt => opt.MapFrom(src => src)); CreateMap() .ConstructUsing(src => src.Provider) .ReverseMap() .ForMember(dest => dest.Provider, opt => opt.MapFrom(src => src)); CreateMap(MemberList.None) .ConstructUsing(src => new ClientClaim(src.Type, src.Value, ClaimValueTypes.String)) .ReverseMap(); CreateMap() .ConstructUsing(src => src.Scope) .ReverseMap() .ForMember(dest => dest.Scope, opt => opt.MapFrom(src => src)); CreateMap() .ConstructUsing(src => src.PostLogoutRedirectUri) .ReverseMap() .ForMember(dest => dest.PostLogoutRedirectUri, opt => opt.MapFrom(src => src)); CreateMap() .ConstructUsing(src => src.RedirectUri) .ReverseMap() .ForMember(dest => dest.RedirectUri, opt => opt.MapFrom(src => src)); CreateMap() .ConstructUsing(src => src.GrantType) .ReverseMap() .ForMember(dest => dest.GrantType, opt => opt.MapFrom(src => src)); CreateMap(MemberList.Destination) .ForMember(dest => dest.Type, opt => opt.Condition(srs => srs != null)) .ReverseMap(); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/ClientMappers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Mappers { /// /// Extension methods to map to/from entity/model for clients. /// public static class ClientMappers { static ClientMappers() { Mapper = new MapperConfiguration(cfg => cfg.AddProfile()) .CreateMapper(); } internal static IMapper Mapper { get; } /// /// Maps an entity to a model. /// /// The entity. /// public static Models.Client ToModel(this Entities.Client entity) { return Mapper.Map(entity); } /// /// Maps a model to an entity. /// /// The model. /// public static Entities.Client ToEntity(this Models.Client model) { return Mapper.Map(model); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/IdentityResourceMapperProfile.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Mappers { /// /// Defines entity/model mapping for identity resources. /// /// public class IdentityResourceMapperProfile : Profile { /// /// /// public IdentityResourceMapperProfile() { CreateMap>() .ReverseMap(); CreateMap(MemberList.Destination) .ConstructUsing(src => new Models.IdentityResource()) .ReverseMap(); CreateMap() .ConstructUsing(x => x.Type) .ReverseMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src)); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/IdentityResourceMappers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework.Mappers { /// /// Extension methods to map to/from entity/model for identity resources. /// public static class IdentityResourceMappers { static IdentityResourceMappers() { Mapper = new MapperConfiguration(cfg => cfg.AddProfile()) .CreateMapper(); } internal static IMapper Mapper { get; } /// /// Maps an entity to a model. /// /// The entity. /// public static Models.IdentityResource ToModel(this IdentityResource entity) { return entity == null ? null : Mapper.Map(entity); } /// /// Maps a model to an entity. /// /// The model. /// public static IdentityResource ToEntity(this Models.IdentityResource model) { return model == null ? null : Mapper.Map(model); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/PersistedGrantMapperProfile.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Mappers { /// /// Defines entity/model mapping for persisted grants. /// /// public class PersistedGrantMapperProfile:Profile { /// /// /// /// public PersistedGrantMapperProfile() { CreateMap(MemberList.Destination) .ReverseMap(); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/PersistedGrantMappers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; namespace IdentityServer8.EntityFramework.Mappers { /// /// Extension methods to map to/from entity/model for persisted grants. /// public static class PersistedGrantMappers { static PersistedGrantMappers() { Mapper = new MapperConfiguration(cfg =>cfg.AddProfile()) .CreateMapper(); } internal static IMapper Mapper { get; } /// /// Maps an entity to a model. /// /// The entity. /// public static PersistedGrant ToModel(this Entities.PersistedGrant entity) { return entity == null ? null : Mapper.Map(entity); } /// /// Maps a model to an entity. /// /// The model. /// public static Entities.PersistedGrant ToEntity(this PersistedGrant model) { return model == null ? null : Mapper.Map(model); } /// /// Updates an entity from a model. /// /// The model. /// The entity. public static void UpdateEntity(this PersistedGrant model, Entities.PersistedGrant entity) { Mapper.Map(model, entity); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/ScopeMapperProfile.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Mappers { /// /// Defines entity/model mapping for scopes. /// /// public class ScopeMapperProfile : Profile { /// /// /// public ScopeMapperProfile() { CreateMap>() .ReverseMap(); CreateMap() .ConstructUsing(x => x.Type) .ReverseMap() .ForMember(dest => dest.Type, opt => opt.MapFrom(src => src)); CreateMap(MemberList.Destination) .ConstructUsing(src => new Models.ApiScope()) .ForMember(x => x.Properties, opts => opts.MapFrom(x => x.Properties)) .ForMember(x => x.UserClaims, opts => opts.MapFrom(x => x.UserClaims)) .ReverseMap(); } } } ================================================ FILE: src/EntityFramework.Storage/src/Mappers/ScopeMappers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework.Mappers { /// /// Extension methods to map to/from entity/model for scopes. /// public static class ScopeMappers { static ScopeMappers() { Mapper = new MapperConfiguration(cfg => cfg.AddProfile()) .CreateMapper(); } internal static IMapper Mapper { get; } /// /// Maps an entity to a model. /// /// The entity. /// public static Models.ApiScope ToModel(this ApiScope entity) { return entity == null ? null : Mapper.Map(entity); } /// /// Maps a model to an entity. /// /// The model. /// public static ApiScope ToEntity(this Models.ApiScope model) { return model == null ? null : Mapper.Map(model); } } } ================================================ FILE: src/EntityFramework.Storage/src/Options/ConfigurationStoreOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Options { /// /// Options for configuring the configuration context. /// public class ConfigurationStoreOptions { /// /// Callback to configure the EF DbContext. /// /// /// The configure database context. /// public Action ConfigureDbContext { get; set; } /// /// Callback in DI resolve the EF DbContextOptions. If set, ConfigureDbContext will not be used. /// /// /// The configure database context. /// public Action ResolveDbContextOptions { get; set; } /// /// Gets or sets the default schema. /// /// /// The default schema. /// public string DefaultSchema { get; set; } = null; /// /// Gets or sets the identity resource table configuration. /// /// /// The identity resource. /// public TableConfiguration IdentityResource { get; set; } = new TableConfiguration("IdentityResources"); /// /// Gets or sets the identity claim table configuration. /// /// /// The identity claim. /// public TableConfiguration IdentityResourceClaim { get; set; } = new TableConfiguration("IdentityResourceClaims"); /// /// Gets or sets the identity resource property table configuration. /// /// /// The client property. /// public TableConfiguration IdentityResourceProperty { get; set; } = new TableConfiguration("IdentityResourceProperties"); /// /// Gets or sets the API resource table configuration. /// /// /// The API resource. /// public TableConfiguration ApiResource { get; set; } = new TableConfiguration("ApiResources"); /// /// Gets or sets the API secret table configuration. /// /// /// The API secret. /// public TableConfiguration ApiResourceSecret { get; set; } = new TableConfiguration("ApiResourceSecrets"); /// /// Gets or sets the API scope table configuration. /// /// /// The API scope. /// public TableConfiguration ApiResourceScope { get; set; } = new TableConfiguration("ApiResourceScopes"); /// /// Gets or sets the API claim table configuration. /// /// /// The API claim. /// public TableConfiguration ApiResourceClaim { get; set; } = new TableConfiguration("ApiResourceClaims"); /// /// Gets or sets the API resource property table configuration. /// /// /// The client property. /// public TableConfiguration ApiResourceProperty { get; set; } = new TableConfiguration("ApiResourceProperties"); /// /// Gets or sets the client table configuration. /// /// /// The client. /// public TableConfiguration Client { get; set; } = new TableConfiguration("Clients"); /// /// Gets or sets the type of the client grant table configuration. /// /// /// The type of the client grant. /// public TableConfiguration ClientGrantType { get; set; } = new TableConfiguration("ClientGrantTypes"); /// /// Gets or sets the client redirect URI table configuration. /// /// /// The client redirect URI. /// public TableConfiguration ClientRedirectUri { get; set; } = new TableConfiguration("ClientRedirectUris"); /// /// Gets or sets the client post logout redirect URI table configuration. /// /// /// The client post logout redirect URI. /// public TableConfiguration ClientPostLogoutRedirectUri { get; set; } = new TableConfiguration("ClientPostLogoutRedirectUris"); /// /// Gets or sets the client scopes table configuration. /// /// /// The client scopes. /// public TableConfiguration ClientScopes { get; set; } = new TableConfiguration("ClientScopes"); /// /// Gets or sets the client secret table configuration. /// /// /// The client secret. /// public TableConfiguration ClientSecret { get; set; } = new TableConfiguration("ClientSecrets"); /// /// Gets or sets the client claim table configuration. /// /// /// The client claim. /// public TableConfiguration ClientClaim { get; set; } = new TableConfiguration("ClientClaims"); /// /// Gets or sets the client IdP restriction table configuration. /// /// /// The client IdP restriction. /// public TableConfiguration ClientIdPRestriction { get; set; } = new TableConfiguration("ClientIdPRestrictions"); /// /// Gets or sets the client cors origin table configuration. /// /// /// The client cors origin. /// public TableConfiguration ClientCorsOrigin { get; set; } = new TableConfiguration("ClientCorsOrigins"); /// /// Gets or sets the client property table configuration. /// /// /// The client property. /// public TableConfiguration ClientProperty { get; set; } = new TableConfiguration("ClientProperties"); /// /// Gets or sets the scope table configuration. /// /// /// The API resource. /// public TableConfiguration ApiScope { get; set; } = new TableConfiguration("ApiScopes"); /// /// Gets or sets the scope claim table configuration. /// /// /// The API scope claim. /// public TableConfiguration ApiScopeClaim { get; set; } = new TableConfiguration("ApiScopeClaims"); /// /// Gets or sets the API resource property table configuration. /// /// /// The client property. /// public TableConfiguration ApiScopeProperty { get; set; } = new TableConfiguration("ApiScopeProperties"); } } ================================================ FILE: src/EntityFramework.Storage/src/Options/OperationalStoreOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Options { /// /// Options for configuring the operational context. /// public class OperationalStoreOptions { /// /// Callback to configure the EF DbContext. /// /// /// The configure database context. /// public Action ConfigureDbContext { get; set; } /// /// Callback in DI resolve the EF DbContextOptions. If set, ConfigureDbContext will not be used. /// /// /// The configure database context. /// public Action ResolveDbContextOptions { get; set; } /// /// Gets or sets the default schema. /// /// /// The default schema. /// public string DefaultSchema { get; set; } = null; /// /// Gets or sets the persisted grants table configuration. /// /// /// The persisted grants. /// public TableConfiguration PersistedGrants { get; set; } = new TableConfiguration("PersistedGrants"); /// /// Gets or sets the device flow codes table configuration. /// /// /// The device flow codes. /// public TableConfiguration DeviceFlowCodes { get; set; } = new TableConfiguration("DeviceCodes"); /// /// Gets or sets a value indicating whether stale entries will be automatically cleaned up from the database. /// This is implemented by periodically connecting to the database (according to the TokenCleanupInterval) from the hosting application. /// Defaults to false. /// /// /// true if [enable token cleanup]; otherwise, false. /// public bool EnableTokenCleanup { get; set; } = false; /// /// Gets or sets the token cleanup interval (in seconds). The default is 3600 (1 hour). /// /// /// The token cleanup interval. /// public int TokenCleanupInterval { get; set; } = 3600; /// /// Gets or sets the number of records to remove at a time. Defaults to 100. /// /// /// The size of the token cleanup batch. /// public int TokenCleanupBatchSize { get; set; } = 100; } } ================================================ FILE: src/EntityFramework.Storage/src/Options/TableConfiguration.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework.Options { /// /// Class to control a table's name and schema. /// public class TableConfiguration { /// /// Initializes a new instance of the class. /// /// The name. public TableConfiguration(string name) { Name = name; } /// /// Initializes a new instance of the class. /// /// The name. /// The schema. public TableConfiguration(string name, string schema) { Name = name; Schema = schema; } /// /// Gets or sets the name. /// /// /// The name. /// public string Name { get; set; } /// /// Gets or sets the schema. /// /// /// The schema. /// public string Schema { get; set; } } } ================================================ FILE: src/EntityFramework.Storage/src/Properties/AssemblyInfo.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Runtime.CompilerServices; [assembly: InternalsVisibleTo("IdentityServer8.EntityFramework.UnitTests, PublicKey = 002400000480000094000000060200000024000052534131000400000100010057b24455efc2a317afb0644a2169c05644e439985c42cf4eb98706779651801add1da073da8b5e253e8d4335d59b3197bb941ebe943c63f7efbc3005c428f0d69b809e86bdc828fa431fae4b71005f26b52a26a3ee5cf0f6fdf744d4534a7a503683123f58e1082828b018245d2e40d8542f72a623c01490d73a5d3ff94a88c5")] [assembly: InternalsVisibleTo("IdentityServer8.EntityFramework.IntegrationTests, PublicKey = 002400000480000094000000060200000024000052534131000400000100010057b24455efc2a317afb0644a2169c05644e439985c42cf4eb98706779651801add1da073da8b5e253e8d4335d59b3197bb941ebe943c63f7efbc3005c428f0d69b809e86bdc828fa431fae4b71005f26b52a26a3ee5cf0f6fdf744d4534a7a503683123f58e1082828b018245d2e40d8542f72a623c01490d73a5d3ff94a88c5")] ================================================ FILE: src/EntityFramework.Storage/src/Stores/ClientStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; namespace IdentityServer8.EntityFramework.Stores { /// /// Implementation of IClientStore thats uses EF. /// /// public class ClientStore : IClientStore { /// /// The DbContext. /// protected readonly IConfigurationDbContext Context; /// /// The logger. /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The context. /// The logger. /// context public ClientStore(IConfigurationDbContext context, ILogger logger) { Context = context ?? throw new ArgumentNullException(nameof(context)); Logger = logger; } /// /// Finds a client by id /// /// The client id /// /// The client /// public virtual async Task FindClientByIdAsync(string clientId) { IQueryable baseQuery = Context.Clients .Where(x => x.ClientId == clientId); var client = (await baseQuery.ToArrayAsync()) .SingleOrDefault(x => x.ClientId == clientId); if (client == null) return null; await baseQuery.Include(x => x.AllowedCorsOrigins).SelectMany(c => c.AllowedCorsOrigins).LoadAsync(); await baseQuery.Include(x => x.AllowedGrantTypes).SelectMany(c => c.AllowedGrantTypes).LoadAsync(); await baseQuery.Include(x => x.AllowedScopes).SelectMany(c => c.AllowedScopes).LoadAsync(); await baseQuery.Include(x => x.Claims).SelectMany(c => c.Claims).LoadAsync(); await baseQuery.Include(x => x.ClientSecrets).SelectMany(c => c.ClientSecrets).LoadAsync(); await baseQuery.Include(x => x.IdentityProviderRestrictions).SelectMany(c => c.IdentityProviderRestrictions).LoadAsync(); await baseQuery.Include(x => x.PostLogoutRedirectUris).SelectMany(c => c.PostLogoutRedirectUris).LoadAsync(); await baseQuery.Include(x => x.Properties).SelectMany(c => c.Properties).LoadAsync(); await baseQuery.Include(x => x.RedirectUris).SelectMany(c => c.RedirectUris).LoadAsync(); var model = client.ToModel(); Logger.LogDebug("{clientId} found in database: {clientIdFound}", Ioc.Sanitizer.Log.Sanitize(clientId), model != null); return model; } } } ================================================ FILE: src/EntityFramework.Storage/src/Stores/DeviceFlowStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; using IdentityServer8.Models; namespace IdentityServer8.EntityFramework.Stores { /// /// Implementation of IDeviceFlowStore thats uses EF. /// /// public class DeviceFlowStore : IDeviceFlowStore { /// /// The DbContext. /// protected readonly IPersistedGrantDbContext Context; /// /// The serializer. /// protected readonly IPersistentGrantSerializer Serializer; /// /// The logger. /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The context. /// The serializer /// The logger. public DeviceFlowStore( IPersistedGrantDbContext context, IPersistentGrantSerializer serializer, ILogger logger) { Context = context; Serializer = serializer; Logger = logger; } /// /// Stores the device authorization request. /// /// The device code. /// The user code. /// The data. /// public virtual async Task StoreDeviceAuthorizationAsync(string deviceCode, string userCode, DeviceCode data) { Context.DeviceFlowCodes.Add(ToEntity(data, deviceCode, userCode)); await Context.SaveChangesAsync(); } /// /// Finds device authorization by user code. /// /// The user code. /// public virtual async Task FindByUserCodeAsync(string userCode) { var deviceFlowCodes = (await Context.DeviceFlowCodes.AsNoTracking().Where(x => x.UserCode == userCode).ToArrayAsync()) .SingleOrDefault(x => x.UserCode == userCode); var model = ToModel(deviceFlowCodes?.Data); Logger.LogDebug("{userCode} found in database: {userCodeFound}", userCode, model != null); return model; } /// /// Finds device authorization by device code. /// /// The device code. /// public virtual async Task FindByDeviceCodeAsync(string deviceCode) { var deviceFlowCodes = (await Context.DeviceFlowCodes.AsNoTracking().Where(x => x.DeviceCode == deviceCode).ToArrayAsync()) .SingleOrDefault(x => x.DeviceCode == deviceCode); var model = ToModel(deviceFlowCodes?.Data); Logger.LogDebug("{deviceCode} found in database: {deviceCodeFound}", deviceCode, model != null); return model; } /// /// Updates device authorization, searching by user code. /// /// The user code. /// The data. /// public virtual async Task UpdateByUserCodeAsync(string userCode, DeviceCode data) { var existing = (await Context.DeviceFlowCodes.Where(x => x.UserCode == userCode).ToArrayAsync()) .SingleOrDefault(x => x.UserCode == userCode); if (existing == null) { Logger.LogError("{userCode} not found in database", userCode); throw new InvalidOperationException("Could not update device code"); } var entity = ToEntity(data, existing.DeviceCode, userCode); Logger.LogDebug("{userCode} found in database", userCode); existing.SubjectId = data.Subject?.FindFirst(JwtClaimTypes.Subject).Value; existing.Data = entity.Data; try { await Context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException ex) { Logger.LogWarning("exception updating {userCode} user code in database: {error}", userCode, ex.Message); } } /// /// Removes the device authorization, searching by device code. /// /// The device code. /// public virtual async Task RemoveByDeviceCodeAsync(string deviceCode) { var deviceFlowCodes = (await Context.DeviceFlowCodes.Where(x => x.DeviceCode == deviceCode).ToArrayAsync()) .SingleOrDefault(x => x.DeviceCode == deviceCode); if(deviceFlowCodes != null) { Logger.LogDebug("removing {deviceCode} device code from database", deviceCode); Context.DeviceFlowCodes.Remove(deviceFlowCodes); try { await Context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException ex) { Logger.LogInformation("exception removing {deviceCode} device code from database: {error}", deviceCode, ex.Message); } } else { Logger.LogDebug("no {deviceCode} device code found in database", deviceCode); } } /// /// Converts a model to an entity. /// /// /// /// /// protected DeviceFlowCodes ToEntity(DeviceCode model, string deviceCode, string userCode) { if (model == null || deviceCode == null || userCode == null) return null; return new DeviceFlowCodes { DeviceCode = deviceCode, UserCode = userCode, ClientId = model.ClientId, SubjectId = model.Subject?.FindFirst(JwtClaimTypes.Subject).Value, CreationTime = model.CreationTime, Expiration = model.CreationTime.AddSeconds(model.Lifetime), Data = Serializer.Serialize(model) }; } /// /// Converts a serialized DeviceCode to a model. /// /// /// protected DeviceCode ToModel(string entity) { if (entity == null) return null; return Serializer.Deserialize(entity); } } } ================================================ FILE: src/EntityFramework.Storage/src/Stores/PersistedGrantStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; namespace IdentityServer8.EntityFramework.Stores { /// /// Implementation of IPersistedGrantStore thats uses EF. /// /// public class PersistedGrantStore : IPersistedGrantStore { /// /// The DbContext. /// protected readonly IPersistedGrantDbContext Context; /// /// The logger. /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The context. /// The logger. public PersistedGrantStore(IPersistedGrantDbContext context, ILogger logger) { Context = context; Logger = logger; } /// public virtual async Task StoreAsync(PersistedGrant token) { var existing = (await Context.PersistedGrants.Where(x => x.Key == token.Key).ToArrayAsync()) .SingleOrDefault(x => x.Key == token.Key); if (existing == null) { Logger.LogDebug("{persistedGrantKey} not found in database", token.Key); var persistedGrant = token.ToEntity(); Context.PersistedGrants.Add(persistedGrant); } else { Logger.LogDebug("{persistedGrantKey} found in database", token.Key); token.UpdateEntity(existing); } try { await Context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException ex) { Logger.LogWarning("exception updating {persistedGrantKey} persisted grant in database: {error}", token.Key, ex.Message); } } /// public virtual async Task GetAsync(string key) { var persistedGrant = (await Context.PersistedGrants.AsNoTracking().Where(x => x.Key == key).ToArrayAsync()) .SingleOrDefault(x => x.Key == key); var model = persistedGrant?.ToModel(); Logger.LogDebug("{persistedGrantKey} found in database: {persistedGrantKeyFound}", key, model != null); return model; } /// public async Task> GetAllAsync(PersistedGrantFilter filter) { filter.Validate(); var persistedGrants = await Filter(Context.PersistedGrants.AsQueryable(), filter).ToArrayAsync(); persistedGrants = Filter(persistedGrants.AsQueryable(), filter).ToArray(); var model = persistedGrants.Select(x => x.ToModel()); Logger.LogDebug("{persistedGrantCount} persisted grants found for {@filter}", persistedGrants.Length, filter); return model; } /// public virtual async Task RemoveAsync(string key) { var persistedGrant = (await Context.PersistedGrants.Where(x => x.Key == key).ToArrayAsync()) .SingleOrDefault(x => x.Key == key); if (persistedGrant!= null) { Logger.LogDebug("removing {persistedGrantKey} persisted grant from database", key); Context.PersistedGrants.Remove(persistedGrant); try { await Context.SaveChangesAsync(); } catch(DbUpdateConcurrencyException ex) { Logger.LogInformation("exception removing {persistedGrantKey} persisted grant from database: {error}", key, ex.Message); } } else { Logger.LogDebug("no {persistedGrantKey} persisted grant found in database", key); } } /// public async Task RemoveAllAsync(PersistedGrantFilter filter) { filter.Validate(); var persistedGrants = await Filter(Context.PersistedGrants.AsQueryable(), filter).ToArrayAsync(); persistedGrants = Filter(persistedGrants.AsQueryable(), filter).ToArray(); Logger.LogDebug("removing {persistedGrantCount} persisted grants from database for {@filter}", persistedGrants.Length, filter); Context.PersistedGrants.RemoveRange(persistedGrants); try { await Context.SaveChangesAsync(); } catch (DbUpdateConcurrencyException ex) { Logger.LogInformation("removing {persistedGrantCount} persisted grants from database for subject {@filter}: {error}", persistedGrants.Length, filter, ex.Message); } } private IQueryable Filter(IQueryable query, PersistedGrantFilter filter) { if (!String.IsNullOrWhiteSpace(filter.ClientId)) { query = query.Where(x => x.ClientId == filter.ClientId); } if (!String.IsNullOrWhiteSpace(filter.SessionId)) { query = query.Where(x => x.SessionId == filter.SessionId); } if (!String.IsNullOrWhiteSpace(filter.SubjectId)) { query = query.Where(x => x.SubjectId == filter.SubjectId); } if (!String.IsNullOrWhiteSpace(filter.Type)) { query = query.Where(x => x.Type == filter.Type); } return query; } } } ================================================ FILE: src/EntityFramework.Storage/src/Stores/ResourceStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; namespace IdentityServer8.EntityFramework.Stores { /// /// Implementation of IResourceStore thats uses EF. /// /// public class ResourceStore : IResourceStore { /// /// The DbContext. /// protected readonly IConfigurationDbContext Context; /// /// The logger. /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The context. /// The logger. /// context public ResourceStore(IConfigurationDbContext context, ILogger logger) { Context = context ?? throw new ArgumentNullException(nameof(context)); Logger = logger; } /// /// Finds the API resources by name. /// /// The names. /// public virtual async Task> FindApiResourcesByNameAsync(IEnumerable apiResourceNames) { if (apiResourceNames == null) throw new ArgumentNullException(nameof(apiResourceNames)); var query = from apiResource in Context.ApiResources where apiResourceNames.Contains(apiResource.Name) select apiResource; var apis = query .Include(x => x.Secrets) .Include(x => x.Scopes) .Include(x => x.UserClaims) .Include(x => x.Properties) .AsNoTracking(); var result = (await apis.ToArrayAsync()) .Where(x => apiResourceNames.Contains(x.Name)) .Select(x => x.ToModel()).ToArray(); if (result.Any()) { Logger.LogDebug("Found {apis} API resource in database", result.Select(x => x.Name)); } else { Logger.LogDebug("Did not find {apis} API resource in database", apiResourceNames); } return result; } /// /// Gets API resources by scope name. /// /// /// public virtual async Task> FindApiResourcesByScopeNameAsync(IEnumerable scopeNames) { var names = scopeNames.ToArray(); var query = from api in Context.ApiResources where api.Scopes.Where(x => names.Contains(x.Scope)).Any() select api; var apis = query .Include(x => x.Secrets) .Include(x => x.Scopes) .Include(x => x.UserClaims) .Include(x => x.Properties) .AsNoTracking(); var results = (await apis.ToArrayAsync()) .Where(api => api.Scopes.Any(x => names.Contains(x.Scope))); var models = results.Select(x => x.ToModel()).ToArray(); Logger.LogDebug("Found {apis} API resources in database", models.Select(x => x.Name)); return models; } /// /// Gets identity resources by scope name. /// /// /// public virtual async Task> FindIdentityResourcesByScopeNameAsync(IEnumerable scopeNames) { var scopes = scopeNames.ToArray(); var query = from identityResource in Context.IdentityResources where scopes.Contains(identityResource.Name) select identityResource; var resources = query .Include(x => x.UserClaims) .Include(x => x.Properties) .AsNoTracking(); var results = (await resources.ToArrayAsync()) .Where(x => scopes.Contains(x.Name)); Logger.LogDebug("Found {scopes} identity scopes in database", results.Select(x => x.Name)); return results.Select(x => x.ToModel()).ToArray(); } /// /// Gets scopes by scope name. /// /// /// public virtual async Task> FindApiScopesByNameAsync(IEnumerable scopeNames) { var scopes = scopeNames.ToArray(); var query = from scope in Context.ApiScopes where scopes.Contains(scope.Name) select scope; var resources = query .Include(x => x.UserClaims) .Include(x => x.Properties) .AsNoTracking(); var results = (await resources.ToArrayAsync()) .Where(x => scopes.Contains(x.Name)); Logger.LogDebug("Found {scopes} scopes in database", results.Select(x => x.Name)); return results.Select(x => x.ToModel()).ToArray(); } /// /// Gets all resources. /// /// public virtual async Task GetAllResourcesAsync() { var identity = Context.IdentityResources .Include(x => x.UserClaims) .Include(x => x.Properties); var apis = Context.ApiResources .Include(x => x.Secrets) .Include(x => x.Scopes) .Include(x => x.UserClaims) .Include(x => x.Properties) .AsNoTracking(); var scopes = Context.ApiScopes .Include(x => x.UserClaims) .Include(x => x.Properties) .AsNoTracking(); var result = new Resources( (await identity.ToArrayAsync()).Select(x => x.ToModel()), (await apis.ToArrayAsync()).Select(x => x.ToModel()), (await scopes.ToArrayAsync()).Select(x => x.ToModel()) ); Logger.LogDebug("Found {scopes} as all scopes, and {apis} as API resources", result.IdentityResources.Select(x=>x.Name).Union(result.ApiScopes.Select(x=>x.Name)), result.ApiResources.Select(x=>x.Name)); return result; } } } ================================================ FILE: src/EntityFramework.Storage/src/TokenCleanup/IOperationalStoreNotification.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Entities; namespace IdentityServer8.EntityFramework { /// /// Interface to model notifications from the TokenCleanup feature. /// public interface IOperationalStoreNotification { /// /// Notification for persisted grants being removed. /// /// /// Task PersistedGrantsRemovedAsync(IEnumerable persistedGrants); /// /// Notification for device codes being removed. /// /// /// Task DeviceCodesRemovedAsync(IEnumerable deviceCodes); } } ================================================ FILE: src/EntityFramework.Storage/src/TokenCleanup/TokenCleanupService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.EntityFramework { /// /// Helper to cleanup stale persisted grants and device codes. /// public class TokenCleanupService { private readonly OperationalStoreOptions _options; private readonly IPersistedGrantDbContext _persistedGrantDbContext; private readonly IOperationalStoreNotification _operationalStoreNotification; private readonly ILogger _logger; /// /// Constructor for TokenCleanupService. /// /// /// /// /// public TokenCleanupService( OperationalStoreOptions options, IPersistedGrantDbContext persistedGrantDbContext, ILogger logger, IOperationalStoreNotification operationalStoreNotification = null) { _options = options ?? throw new ArgumentNullException(nameof(options)); if (_options.TokenCleanupBatchSize < 1) throw new ArgumentException("Token cleanup batch size interval must be at least 1"); _persistedGrantDbContext = persistedGrantDbContext ?? throw new ArgumentNullException(nameof(persistedGrantDbContext)); _logger = logger ?? throw new ArgumentNullException(nameof(logger)); _operationalStoreNotification = operationalStoreNotification; } /// /// Method to clear expired persisted grants. /// /// public async Task RemoveExpiredGrantsAsync() { try { _logger.LogTrace("Querying for expired grants to remove"); await RemoveGrantsAsync(); await RemoveDeviceCodesAsync(); } catch (Exception ex) { _logger.LogError("Exception removing expired grants: {exception}", ex.Message); } } /// /// Removes the stale persisted grants. /// /// protected virtual async Task RemoveGrantsAsync() { var found = Int32.MaxValue; while (found >= _options.TokenCleanupBatchSize) { var expiredGrants = await _persistedGrantDbContext.PersistedGrants .Where(x => x.Expiration < DateTime.UtcNow) .OrderBy(x => x.Expiration) .Take(_options.TokenCleanupBatchSize) .ToArrayAsync(); found = expiredGrants.Length; _logger.LogInformation("Removing {grantCount} grants", found); if (found > 0) { _persistedGrantDbContext.PersistedGrants.RemoveRange(expiredGrants); await SaveChangesAsync(); if (_operationalStoreNotification != null) { await _operationalStoreNotification.PersistedGrantsRemovedAsync(expiredGrants); } } } } /// /// Removes the stale device codes. /// /// protected virtual async Task RemoveDeviceCodesAsync() { var found = Int32.MaxValue; while (found >= _options.TokenCleanupBatchSize) { var expiredCodes = await _persistedGrantDbContext.DeviceFlowCodes .Where(x => x.Expiration < DateTime.UtcNow) .OrderBy(x => x.Expiration) .Take(_options.TokenCleanupBatchSize) .ToArrayAsync(); found = expiredCodes.Length; _logger.LogInformation("Removing {deviceCodeCount} device flow codes", found); if (found > 0) { _persistedGrantDbContext.DeviceFlowCodes.RemoveRange(expiredCodes); await SaveChangesAsync(); if (_operationalStoreNotification != null) { await _operationalStoreNotification.DeviceCodesRemovedAsync(expiredCodes); } } } } private async Task SaveChangesAsync() { var count = 3; while (count > 0) { try { await _persistedGrantDbContext.SaveChangesAsync(); return; } catch (DbUpdateConcurrencyException ex) { count--; // we get this if/when someone else already deleted the records // we want to essentially ignore this, and keep working _logger.LogDebug("Concurrency exception removing expired grants: {exception}", ex.Message); foreach (var entry in ex.Entries) { // mark this entry as not attached anymore so we don't try to re-delete entry.State = EntityState.Detached; } } } _logger.LogDebug("Too many concurrency exceptions. Exiting."); } } } ================================================ FILE: src/EntityFramework.Storage/test/Directory.Build.props ================================================ ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/DatabaseProviderBuilder.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore; namespace IdentityServer8.EntityFramework.IntegrationTests; /// /// Helper methods to initialize DbContextOptions for the specified database provider and context. /// public class DatabaseProviderBuilder { public static DbContextOptions BuildInMemory(string name) where T : DbContext { var builder = new DbContextOptionsBuilder(); builder.UseInMemoryDatabase(name); return builder.Options; } public static DbContextOptions BuildSqlite(string name) where T : DbContext { var builder = new DbContextOptionsBuilder(); builder.UseSqlite($"Filename=./Test.IdentityServer8.EntityFramework-3.1.0.{name}.db"); return builder.Options; } public static DbContextOptions BuildLocalDb(string name) where T : DbContext { var builder = new DbContextOptionsBuilder(); builder.UseSqlServer( $@"Data Source=(LocalDb)\MSSQLLocalDB;database=Test.IdentityServer8.EntityFramework-3.1.0.{name};trusted_connection=yes;"); return builder.Options; } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/DatabaseProviderFixture.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore; namespace IdentityServer8.EntityFramework.IntegrationTests; /// /// xUnit ClassFixture for creating and deleting integration test databases. /// /// DbContext of Type T public class DatabaseProviderFixture : IDisposable where T : DbContext { public object StoreOptions; public List> Options; public void Dispose() { if (Options != null) // null check since fixtures are created even when tests are skipped { foreach (var option in Options.ToList()) { using (var context = (T)Activator.CreateInstance(typeof(T), option, StoreOptions)) { context.Database.EnsureDeleted(); } } } } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/DbContexts/ClientDbContextTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.DbContexts; using Microsoft.EntityFrameworkCore; using System.Linq; using IdentityServer8.EntityFramework.Entities; using IdentityServer8.EntityFramework.Options; using Xunit; namespace IdentityServer8.EntityFramework.IntegrationTests.DbContexts; public class ClientDbContextTests : IntegrationTest { public ClientDbContextTests(DatabaseProviderFixture fixture) : base(fixture) { foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList()) { using (var context = new ConfigurationDbContext(options, StoreOptions)) context.Database.EnsureCreated(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public void CanAddAndDeleteClientScopes(DbContextOptions options) { using (var db = new ConfigurationDbContext(options, StoreOptions)) { db.Clients.Add(new Client { ClientId = "test-client-scopes", ClientName = "Test Client" }); db.SaveChanges(); } using (var db = new ConfigurationDbContext(options, StoreOptions)) { // explicit include due to lack of EF Core lazy loading var client = db.Clients.Include(x => x.AllowedScopes).First(); client.AllowedScopes.Add(new ClientScope { Scope = "test" }); db.SaveChanges(); } using (var db = new ConfigurationDbContext(options, StoreOptions)) { var client = db.Clients.Include(x => x.AllowedScopes).First(); var scope = client.AllowedScopes.First(); client.AllowedScopes.Remove(scope); db.SaveChanges(); } using (var db = new ConfigurationDbContext(options, StoreOptions)) { var client = db.Clients.Include(x => x.AllowedScopes).First(); Assert.Empty(client.AllowedScopes); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public void CanAddAndDeleteClientRedirectUri(DbContextOptions options) { using (var db = new ConfigurationDbContext(options, StoreOptions)) { db.Clients.Add(new Client { ClientId = "test-client", ClientName = "Test Client" }); db.SaveChanges(); } using (var db = new ConfigurationDbContext(options, StoreOptions)) { var client = db.Clients.Include(x => x.RedirectUris).First(); client.RedirectUris.Add(new ClientRedirectUri { RedirectUri = "https://redirect-uri-1" }); db.SaveChanges(); } using (var db = new ConfigurationDbContext(options, StoreOptions)) { var client = db.Clients.Include(x => x.RedirectUris).First(); var redirectUri = client.RedirectUris.First(); client.RedirectUris.Remove(redirectUri); db.SaveChanges(); } using (var db = new ConfigurationDbContext(options, StoreOptions)) { var client = db.Clients.Include(x => x.RedirectUris).First(); Assert.Empty(client.RedirectUris); } } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/FakeLogger.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.Extensions.Logging; namespace IdentityServer8.EntityFramework.IntegrationTests; public class FakeLogger : FakeLogger, ILogger { public static ILogger Create() { return new FakeLogger(); } } public class FakeLogger : ILogger, IDisposable { public IDisposable BeginScope(TState state) { return this; } public void Dispose() { } public bool IsEnabled(LogLevel logLevel) { return false; } public void Log(LogLevel logLevel, EventId eventId, TState state, Exception exception, Func formatter) { } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/IdentityServer8.EntityFramework.IntegrationTests.csproj ================================================ ../../../../key.snk true true runtime; build; native; contentfiles; analyzers; buildtransitive all ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/IntegrationTest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.Configuration; using System.Runtime.InteropServices; using Xunit; namespace IdentityServer8.EntityFramework.IntegrationTests; /// /// Base class for integration tests, responsible for initializing test database providers & an xUnit class fixture /// /// The type of the class. /// The type of the database context. /// The type of the store option. /// public class IntegrationTest : IClassFixture> where TDbContext : DbContext { public static readonly TheoryData> TestDatabaseProviders; protected readonly TStoreOption StoreOptions = Activator.CreateInstance(); static IntegrationTest() { var config = new ConfigurationBuilder() .AddEnvironmentVariables() .Build(); if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { Console.WriteLine($"Running Local Tests for {typeof(TClass).Name}"); TestDatabaseProviders = new TheoryData> { DatabaseProviderBuilder.BuildInMemory(typeof(TClass).Name), //DatabaseProviderBuilder.BuildSqlite(typeof(TClass).Name), //DatabaseProviderBuilder.BuildLocalDb(typeof(TClass).Name) }; } else { TestDatabaseProviders = new TheoryData> { DatabaseProviderBuilder.BuildInMemory(typeof(TClass).Name), DatabaseProviderBuilder.BuildSqlite(typeof(TClass).Name) }; Console.WriteLine("Skipping DB integration tests on non-Windows"); } } protected IntegrationTest(DatabaseProviderFixture fixture) { fixture.Options = TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList(); fixture.StoreOptions = StoreOptions; } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/Stores/ClientStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer8.EntityFramework.DbContexts; using IdentityServer8.EntityFramework.Mappers; using IdentityServer8.EntityFramework.Options; using IdentityServer8.EntityFramework.Stores; using IdentityServer8.Models; using Microsoft.EntityFrameworkCore; using Xunit; using Xunit.Sdk; namespace IdentityServer8.EntityFramework.IntegrationTests.Stores; public class ClientStoreTests : IntegrationTest { public ClientStoreTests(DatabaseProviderFixture fixture) : base(fixture) { foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions) y)).ToList()) { using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.Database.EnsureCreated(); } } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindClientByIdAsync_WhenClientDoesNotExist_ExpectNull(DbContextOptions options) { using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ClientStore(context, FakeLogger.Create()); var client = await store.FindClientByIdAsync(Guid.NewGuid().ToString()); client.Should().BeNull(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindClientByIdAsync_WhenClientExists_ExpectClientRetured(DbContextOptions options) { var testClient = new Client { ClientId = "test_client", ClientName = "Test Client" }; using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.Clients.Add(testClient.ToEntity()); context.SaveChanges(); } Client client; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ClientStore(context, FakeLogger.Create()); client = await store.FindClientByIdAsync(testClient.ClientId); } client.Should().NotBeNull(); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindClientByIdAsync_WhenClientExistsWithCollections_ExpectClientReturnedCollections(DbContextOptions options) { var testClient = new Client { ClientId = "properties_test_client", ClientName = "Properties Test Client", AllowedCorsOrigins = {"https://localhost"}, AllowedGrantTypes = GrantTypes.HybridAndClientCredentials, AllowedScopes = {"openid", "profile", "api1"}, Claims = {new ClientClaim("test", "value")}, ClientSecrets = {new Secret("secret".Sha256())}, IdentityProviderRestrictions = {"AD"}, PostLogoutRedirectUris = {"https://locahost/signout-callback"}, Properties = {{"foo1", "bar1"}, {"foo2", "bar2"},}, RedirectUris = {"https://locahost/signin"} }; using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.Clients.Add(testClient.ToEntity()); context.SaveChanges(); } Client client; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ClientStore(context, FakeLogger.Create()); client = await store.FindClientByIdAsync(testClient.ClientId); } client.Should().BeEquivalentTo(testClient); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindClientByIdAsync_WhenClientsExistWithManyCollections_ExpectClientReturnedInUnderFiveSeconds(DbContextOptions options) { var testClient = new Client { ClientId = "test_client_with_uris", ClientName = "Test client with URIs", AllowedScopes = {"openid", "profile", "api1"}, AllowedGrantTypes = GrantTypes.CodeAndClientCredentials }; for (int i = 0; i < 50; i++) { testClient.RedirectUris.Add($"https://localhost/{i}"); testClient.PostLogoutRedirectUris.Add($"https://localhost/{i}"); testClient.AllowedCorsOrigins.Add($"https://localhost:{i}"); } using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.Clients.Add(testClient.ToEntity()); for (int i = 0; i < 50; i++) { context.Clients.Add(new Client { ClientId = testClient.ClientId + i, ClientName = testClient.ClientName, AllowedScopes = testClient.AllowedScopes, AllowedGrantTypes = testClient.AllowedGrantTypes, RedirectUris = testClient.RedirectUris, PostLogoutRedirectUris = testClient.PostLogoutRedirectUris, AllowedCorsOrigins = testClient.AllowedCorsOrigins, }.ToEntity()); } context.SaveChanges(); } using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ClientStore(context, FakeLogger.Create()); const int timeout = 5000; var task = Task.Run(() => store.FindClientByIdAsync(testClient.ClientId)); if (await Task.WhenAny(task, Task.Delay(timeout)) == task) { var client = await task; client.Should().BeEquivalentTo(testClient); } else { throw new TestTimeoutException(timeout); } } } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/Stores/DeviceFlowStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel; using IdentityServer8.EntityFramework.DbContexts; using IdentityServer8.EntityFramework.Entities; using IdentityServer8.EntityFramework.Options; using IdentityServer8.EntityFramework.Stores; using IdentityServer8.Models; using IdentityServer8.Stores.Serialization; using Microsoft.EntityFrameworkCore; using Microsoft.EntityFrameworkCore.InMemory.Infrastructure.Internal; using System.Security.Claims; using Xunit; namespace IdentityServer8.EntityFramework.IntegrationTests.Stores; public class DeviceFlowStoreTests : IntegrationTest { private readonly IPersistentGrantSerializer serializer = new PersistentGrantSerializer(); public DeviceFlowStoreTests(DatabaseProviderFixture fixture) : base(fixture) { foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList()) { using (var context = new PersistedGrantDbContext(options, StoreOptions)) context.Database.EnsureCreated(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task StoreDeviceAuthorizationAsync_WhenSuccessful_ExpectDeviceCodeAndUserCodeStored(DbContextOptions options) { var deviceCode = Guid.NewGuid().ToString(); var userCode = Guid.NewGuid().ToString(); var data = new DeviceCode { ClientId = Guid.NewGuid().ToString(), CreationTime = DateTime.UtcNow, Lifetime = 300 }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); await store.StoreDeviceAuthorizationAsync(deviceCode, userCode, data); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundDeviceFlowCodes = context.DeviceFlowCodes.FirstOrDefault(x => x.DeviceCode == deviceCode); foundDeviceFlowCodes.Should().NotBeNull(); foundDeviceFlowCodes?.DeviceCode.Should().Be(deviceCode); foundDeviceFlowCodes?.UserCode.Should().Be(userCode); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task StoreDeviceAuthorizationAsync_WhenSuccessful_ExpectDataStored(DbContextOptions options) { var deviceCode = Guid.NewGuid().ToString(); var userCode = Guid.NewGuid().ToString(); var data = new DeviceCode { ClientId = Guid.NewGuid().ToString(), CreationTime = DateTime.UtcNow, Lifetime = 300 }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); await store.StoreDeviceAuthorizationAsync(deviceCode, userCode, data); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundDeviceFlowCodes = context.DeviceFlowCodes.FirstOrDefault(x => x.DeviceCode == deviceCode); foundDeviceFlowCodes.Should().NotBeNull(); var deserializedData = new PersistentGrantSerializer().Deserialize(foundDeviceFlowCodes?.Data); deserializedData.CreationTime.Should().BeCloseTo(data.CreationTime, TimeSpan.FromMilliseconds(100)); deserializedData.ClientId.Should().Be(data.ClientId); deserializedData.Lifetime.Should().Be(data.Lifetime); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task StoreDeviceAuthorizationAsync_WhenUserCodeAlreadyExists_ExpectException(DbContextOptions options) { var existingUserCode = $"user_{Guid.NewGuid().ToString()}"; var deviceCodeData = new DeviceCode { ClientId = "device_flow", RequestedScopes = new[] { "openid", "api1" }, CreationTime = new DateTime(2018, 10, 19, 16, 14, 29), Lifetime = 300, IsOpenId = true, Subject = new ClaimsPrincipal(new ClaimsIdentity( new List { new Claim(JwtClaimTypes.Subject, $"sub_{Guid.NewGuid().ToString()}") })) }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.Add(new DeviceFlowCodes { DeviceCode = $"device_{Guid.NewGuid().ToString()}", UserCode = existingUserCode, ClientId = deviceCodeData.ClientId, SubjectId = deviceCodeData.Subject.FindFirst(JwtClaimTypes.Subject).Value, CreationTime = deviceCodeData.CreationTime, Expiration = deviceCodeData.CreationTime.AddSeconds(deviceCodeData.Lifetime), Data = serializer.Serialize(deviceCodeData) }); context.SaveChanges(); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); // skip odd behaviour of in-memory provider if (options.Extensions.All(x => x.GetType() != typeof(InMemoryOptionsExtension))) { await Assert.ThrowsAsync(() => store.StoreDeviceAuthorizationAsync($"device_{Guid.NewGuid().ToString()}", existingUserCode, deviceCodeData)); } } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task StoreDeviceAuthorizationAsync_WhenDeviceCodeAlreadyExists_ExpectException(DbContextOptions options) { var existingDeviceCode = $"device_{Guid.NewGuid().ToString()}"; var deviceCodeData = new DeviceCode { ClientId = "device_flow", RequestedScopes = new[] { "openid", "api1" }, CreationTime = new DateTime(2018, 10, 19, 16, 14, 29), Lifetime = 300, IsOpenId = true, Subject = new ClaimsPrincipal(new ClaimsIdentity( new List { new Claim(JwtClaimTypes.Subject, $"sub_{Guid.NewGuid().ToString()}") })) }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.Add(new DeviceFlowCodes { DeviceCode = existingDeviceCode, UserCode = $"user_{Guid.NewGuid().ToString()}", ClientId = deviceCodeData.ClientId, SubjectId = deviceCodeData.Subject.FindFirst(JwtClaimTypes.Subject).Value, CreationTime = deviceCodeData.CreationTime, Expiration = deviceCodeData.CreationTime.AddSeconds(deviceCodeData.Lifetime), Data = serializer.Serialize(deviceCodeData) }); context.SaveChanges(); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); // skip odd behaviour of in-memory provider if (options.Extensions.All(x => x.GetType() != typeof(InMemoryOptionsExtension))) { await Assert.ThrowsAsync(() => store.StoreDeviceAuthorizationAsync(existingDeviceCode, $"user_{Guid.NewGuid().ToString()}", deviceCodeData)); } } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindByUserCodeAsync_WhenUserCodeExists_ExpectDataRetrievedCorrectly(DbContextOptions options) { var testDeviceCode = $"device_{Guid.NewGuid().ToString()}"; var testUserCode = $"user_{Guid.NewGuid().ToString()}"; var expectedSubject = $"sub_{Guid.NewGuid().ToString()}"; var expectedDeviceCodeData = new DeviceCode { ClientId = "device_flow", RequestedScopes = new[] { "openid", "api1" }, CreationTime = new DateTime(2018, 10, 19, 16, 14, 29), Lifetime = 300, IsOpenId = true, Subject = new ClaimsPrincipal(new ClaimsIdentity(new List { new Claim(JwtClaimTypes.Subject, expectedSubject) })) }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.Add(new DeviceFlowCodes { DeviceCode = testDeviceCode, UserCode = testUserCode, ClientId = expectedDeviceCodeData.ClientId, SubjectId = expectedDeviceCodeData.Subject.FindFirst(JwtClaimTypes.Subject).Value, CreationTime = expectedDeviceCodeData.CreationTime, Expiration = expectedDeviceCodeData.CreationTime.AddSeconds(expectedDeviceCodeData.Lifetime), Data = serializer.Serialize(expectedDeviceCodeData) }); context.SaveChanges(); } DeviceCode code; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); code = await store.FindByUserCodeAsync(testUserCode); } code.Should().BeEquivalentTo(expectedDeviceCodeData, assertionOptions => assertionOptions.Excluding(x=> x.Subject)); code.Subject.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject && x.Value == expectedSubject).Should().NotBeNull(); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindByUserCodeAsync_WhenUserCodeDoesNotExist_ExpectNull(DbContextOptions options) { using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); var code = await store.FindByUserCodeAsync($"user_{Guid.NewGuid().ToString()}"); code.Should().BeNull(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindByDeviceCodeAsync_WhenDeviceCodeExists_ExpectDataRetrievedCorrectly(DbContextOptions options) { var testDeviceCode = $"device_{Guid.NewGuid().ToString()}"; var testUserCode = $"user_{Guid.NewGuid().ToString()}"; var expectedSubject = $"sub_{Guid.NewGuid().ToString()}"; var expectedDeviceCodeData = new DeviceCode { ClientId = "device_flow", RequestedScopes = new[] { "openid", "api1" }, CreationTime = new DateTime(2018, 10, 19, 16, 14, 29), Lifetime = 300, IsOpenId = true, Subject = new ClaimsPrincipal(new ClaimsIdentity(new List { new Claim(JwtClaimTypes.Subject, expectedSubject) })) }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.Add(new DeviceFlowCodes { DeviceCode = testDeviceCode, UserCode = testUserCode, ClientId = expectedDeviceCodeData.ClientId, SubjectId = expectedDeviceCodeData.Subject.FindFirst(JwtClaimTypes.Subject).Value, CreationTime = expectedDeviceCodeData.CreationTime, Expiration = expectedDeviceCodeData.CreationTime.AddSeconds(expectedDeviceCodeData.Lifetime), Data = serializer.Serialize(expectedDeviceCodeData) }); context.SaveChanges(); } DeviceCode code; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); code = await store.FindByDeviceCodeAsync(testDeviceCode); } code.Should().BeEquivalentTo(expectedDeviceCodeData, assertionOptions => assertionOptions.Excluding(x => x.Subject)); code.Subject.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject && x.Value == expectedSubject).Should().NotBeNull(); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindByDeviceCodeAsync_WhenDeviceCodeDoesNotExist_ExpectNull(DbContextOptions options) { using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); var code = await store.FindByDeviceCodeAsync($"device_{Guid.NewGuid().ToString()}"); code.Should().BeNull(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task UpdateByUserCodeAsync_WhenDeviceCodeAuthorized_ExpectSubjectAndDataUpdated(DbContextOptions options) { var testDeviceCode = $"device_{Guid.NewGuid().ToString()}"; var testUserCode = $"user_{Guid.NewGuid().ToString()}"; var expectedSubject = $"sub_{Guid.NewGuid().ToString()}"; var unauthorizedDeviceCode = new DeviceCode { ClientId = "device_flow", RequestedScopes = new[] {"openid", "api1"}, CreationTime = new DateTime(2018, 10, 19, 16, 14, 29), Lifetime = 300, IsOpenId = true }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.Add(new DeviceFlowCodes { DeviceCode = testDeviceCode, UserCode = testUserCode, ClientId = unauthorizedDeviceCode.ClientId, CreationTime = unauthorizedDeviceCode.CreationTime, Expiration = unauthorizedDeviceCode.CreationTime.AddSeconds(unauthorizedDeviceCode.Lifetime), Data = serializer.Serialize(unauthorizedDeviceCode) }); context.SaveChanges(); } var authorizedDeviceCode = new DeviceCode { ClientId = unauthorizedDeviceCode.ClientId, RequestedScopes = unauthorizedDeviceCode.RequestedScopes, AuthorizedScopes = unauthorizedDeviceCode.RequestedScopes, Subject = new ClaimsPrincipal(new ClaimsIdentity(new List { new Claim(JwtClaimTypes.Subject, expectedSubject) })), IsAuthorized = true, IsOpenId = true, CreationTime = new DateTime(2018, 10, 19, 16, 14, 29), Lifetime = 300 }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); await store.UpdateByUserCodeAsync(testUserCode, authorizedDeviceCode); } DeviceFlowCodes updatedCodes; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { updatedCodes = context.DeviceFlowCodes.Single(x => x.UserCode == testUserCode); } // should be unchanged updatedCodes.DeviceCode.Should().Be(testDeviceCode); updatedCodes.ClientId.Should().Be(unauthorizedDeviceCode.ClientId); updatedCodes.CreationTime.Should().Be(unauthorizedDeviceCode.CreationTime); updatedCodes.Expiration.Should().Be(unauthorizedDeviceCode.CreationTime.AddSeconds(authorizedDeviceCode.Lifetime)); // should be changed var parsedCode = serializer.Deserialize(updatedCodes.Data); parsedCode.Should().BeEquivalentTo(authorizedDeviceCode, assertionOptions => assertionOptions.Excluding(x => x.Subject)); parsedCode.Subject.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.Subject && x.Value == expectedSubject).Should().NotBeNull(); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveByDeviceCodeAsync_WhenDeviceCodeExists_ExpectDeviceCodeDeleted(DbContextOptions options) { var testDeviceCode = $"device_{Guid.NewGuid().ToString()}"; var testUserCode = $"user_{Guid.NewGuid().ToString()}"; var existingDeviceCode = new DeviceCode { ClientId = "device_flow", RequestedScopes = new[] { "openid", "api1" }, CreationTime = new DateTime(2018, 10, 19, 16, 14, 29), Lifetime = 300, IsOpenId = true }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.Add(new DeviceFlowCodes { DeviceCode = testDeviceCode, UserCode = testUserCode, ClientId = existingDeviceCode.ClientId, CreationTime = existingDeviceCode.CreationTime, Expiration = existingDeviceCode.CreationTime.AddSeconds(existingDeviceCode.Lifetime), Data = serializer.Serialize(existingDeviceCode) }); context.SaveChanges(); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); await store.RemoveByDeviceCodeAsync(testDeviceCode); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.FirstOrDefault(x => x.UserCode == testUserCode).Should().BeNull(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveByDeviceCodeAsync_WhenDeviceCodeDoesNotExists_ExpectSuccess(DbContextOptions options) { using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new DeviceFlowStore(context, new PersistentGrantSerializer(), FakeLogger.Create()); await store.RemoveByDeviceCodeAsync($"device_{Guid.NewGuid().ToString()}"); } } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/Stores/PersistedGrantStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer8.EntityFramework.DbContexts; using IdentityServer8.EntityFramework.Mappers; using IdentityServer8.EntityFramework.Options; using IdentityServer8.EntityFramework.Stores; using IdentityServer8.Models; using IdentityServer8.Stores; using Microsoft.EntityFrameworkCore; using Xunit; namespace IdentityServer8.EntityFramework.IntegrationTests.Stores; public class PersistedGrantStoreTests : IntegrationTest { public PersistedGrantStoreTests(DatabaseProviderFixture fixture) : base(fixture) { foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList()) { using (var context = new PersistedGrantDbContext(options, StoreOptions)) context.Database.EnsureCreated(); } } private static PersistedGrant CreateTestObject(string sub = null, string clientId = null, string sid = null, string type = null) { return new PersistedGrant { Key = Guid.NewGuid().ToString(), Type = type ?? "authorization_code", ClientId = clientId ?? Guid.NewGuid().ToString(), SubjectId = sub ?? Guid.NewGuid().ToString(), SessionId = sid ?? Guid.NewGuid().ToString(), CreationTime = new DateTime(2016, 08, 01), Expiration = new DateTime(2016, 08, 31), Data = Guid.NewGuid().ToString() }; } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task StoreAsync_WhenPersistedGrantStored_ExpectSuccess(DbContextOptions options) { var persistedGrant = CreateTestObject(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.StoreAsync(persistedGrant); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundGrant = context.PersistedGrants.FirstOrDefault(x => x.Key == persistedGrant.Key); Assert.NotNull(foundGrant); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task GetAsync_WithKeyAndPersistedGrantExists_ExpectPersistedGrantReturned(DbContextOptions options) { var persistedGrant = CreateTestObject(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(persistedGrant.ToEntity()); context.SaveChanges(); } PersistedGrant foundPersistedGrant; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); foundPersistedGrant = await store.GetAsync(persistedGrant.Key); } Assert.NotNull(foundPersistedGrant); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task GetAllAsync_WithSubAndTypeAndPersistedGrantExists_ExpectPersistedGrantReturned(DbContextOptions options) { var persistedGrant = CreateTestObject(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(persistedGrant.ToEntity()); context.SaveChanges(); } IList foundPersistedGrants; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); foundPersistedGrants = (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = persistedGrant.SubjectId })).ToList(); } Assert.NotNull(foundPersistedGrants); Assert.NotEmpty(foundPersistedGrants); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task GetAllAsync_Should_Filter(DbContextOptions options) { using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c1", sid: "s1", type: "t1").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c1", sid: "s1", type: "t2").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c1", sid: "s2", type: "t1").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c1", sid: "s2", type: "t2").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c2", sid: "s1", type: "t1").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c2", sid: "s1", type: "t2").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c2", sid: "s2", type: "t1").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c2", sid: "s2", type: "t2").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c3", sid: "s3", type: "t3").ToEntity()); context.PersistedGrants.Add(CreateTestObject().ToEntity()); context.SaveChanges(); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1" })).ToList().Count.Should().Be(9); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub2" })).ToList().Count.Should().Be(0); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c1" })).ToList().Count.Should().Be(4); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c2" })).ToList().Count.Should().Be(4); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c3" })).ToList().Count.Should().Be(1); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c4" })).ToList().Count.Should().Be(0); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c1", SessionId = "s1" })).ToList().Count.Should().Be(2); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c3", SessionId = "s1" })).ToList().Count.Should().Be(0); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c1", SessionId = "s1", Type = "t1" })).ToList().Count.Should().Be(1); (await store.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c1", SessionId = "s1", Type = "t3" })).ToList().Count.Should().Be(0); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveAsync_WhenKeyOfExistingReceived_ExpectGrantDeleted(DbContextOptions options) { var persistedGrant = CreateTestObject(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(persistedGrant.ToEntity()); context.SaveChanges(); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAsync(persistedGrant.Key); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundGrant = context.PersistedGrants.FirstOrDefault(x => x.Key == persistedGrant.Key); Assert.Null(foundGrant); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveAllAsync_WhenSubIdAndClientIdOfExistingReceived_ExpectGrantDeleted(DbContextOptions options) { var persistedGrant = CreateTestObject(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(persistedGrant.ToEntity()); context.SaveChanges(); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = persistedGrant.SubjectId, ClientId = persistedGrant.ClientId }); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundGrant = context.PersistedGrants.FirstOrDefault(x => x.Key == persistedGrant.Key); Assert.Null(foundGrant); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveAllAsync_WhenSubIdClientIdAndTypeOfExistingReceived_ExpectGrantDeleted(DbContextOptions options) { var persistedGrant = CreateTestObject(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(persistedGrant.ToEntity()); context.SaveChanges(); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = persistedGrant.SubjectId, ClientId = persistedGrant.ClientId, Type = persistedGrant.Type }); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundGrant = context.PersistedGrants.FirstOrDefault(x => x.Key == persistedGrant.Key); Assert.Null(foundGrant); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveAllAsync_Should_Filter(DbContextOptions options) { void PopulateDb() { using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.RemoveRange(context.PersistedGrants.ToArray()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c1", sid: "s1", type: "t1").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c1", sid: "s1", type: "t2").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c1", sid: "s2", type: "t1").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c1", sid: "s2", type: "t2").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c2", sid: "s1", type: "t1").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c2", sid: "s1", type: "t2").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c2", sid: "s2", type: "t1").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c2", sid: "s2", type: "t2").ToEntity()); context.PersistedGrants.Add(CreateTestObject(sub: "sub1", clientId: "c3", sid: "s3", type: "t3").ToEntity()); context.PersistedGrants.Add(CreateTestObject().ToEntity()); context.SaveChanges(); } } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1" }); context.PersistedGrants.Count().Should().Be(1); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub2" }); context.PersistedGrants.Count().Should().Be(10); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c1" }); context.PersistedGrants.Count().Should().Be(6); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c2" }); context.PersistedGrants.Count().Should().Be(6); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c3" }); context.PersistedGrants.Count().Should().Be(9); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c4" }); context.PersistedGrants.Count().Should().Be(10); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c1", SessionId = "s1" }); context.PersistedGrants.Count().Should().Be(8); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c3", SessionId = "s1" }); context.PersistedGrants.Count().Should().Be(10); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c1", SessionId = "s1", Type = "t1" }); context.PersistedGrants.Count().Should().Be(9); } PopulateDb(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "c1", SessionId = "s1", Type = "t3" }); context.PersistedGrants.Count().Should().Be(10); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task Store_should_create_new_record_if_key_does_not_exist(DbContextOptions options) { var persistedGrant = CreateTestObject(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundGrant = context.PersistedGrants.FirstOrDefault(x => x.Key == persistedGrant.Key); Assert.Null(foundGrant); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); await store.StoreAsync(persistedGrant); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundGrant = context.PersistedGrants.FirstOrDefault(x => x.Key == persistedGrant.Key); Assert.NotNull(foundGrant); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task Store_should_update_record_if_key_already_exists(DbContextOptions options) { var persistedGrant = CreateTestObject(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(persistedGrant.ToEntity()); context.SaveChanges(); } var newDate = persistedGrant.Expiration.Value.AddHours(1); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var store = new PersistedGrantStore(context, FakeLogger.Create()); persistedGrant.Expiration = newDate; await store.StoreAsync(persistedGrant); } using (var context = new PersistedGrantDbContext(options, StoreOptions)) { var foundGrant = context.PersistedGrants.FirstOrDefault(x => x.Key == persistedGrant.Key); Assert.NotNull(foundGrant); Assert.Equal(newDate, persistedGrant.Expiration); } } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/Stores/ResourceStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityModel; using IdentityServer8.EntityFramework.DbContexts; using IdentityServer8.EntityFramework.Mappers; using IdentityServer8.EntityFramework.Options; using IdentityServer8.EntityFramework.Stores; using IdentityServer8.Models; using Microsoft.EntityFrameworkCore; using Xunit; namespace IdentityServer8.EntityFramework.IntegrationTests.Stores; public class ScopeStoreTests : IntegrationTest { public ScopeStoreTests(DatabaseProviderFixture fixture) : base(fixture) { foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList()) { using (var context = new ConfigurationDbContext(options, StoreOptions)) context.Database.EnsureCreated(); } } private static IdentityResource CreateIdentityTestResource() { return new IdentityResource() { Name = Guid.NewGuid().ToString(), DisplayName = Guid.NewGuid().ToString(), Description = Guid.NewGuid().ToString(), ShowInDiscoveryDocument = true, UserClaims = { JwtClaimTypes.Subject, JwtClaimTypes.Name, } }; } private static ApiResource CreateApiResourceTestResource() { return new ApiResource() { Name = Guid.NewGuid().ToString(), ApiSecrets = new List { new Secret("secret".ToSha256()) }, Scopes = { Guid.NewGuid().ToString() }, UserClaims = { Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), } }; } private static ApiScope CreateApiScopeTestResource() { return new ApiScope() { Name = Guid.NewGuid().ToString(), UserClaims = { Guid.NewGuid().ToString(), Guid.NewGuid().ToString(), } }; } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindApiResourcesByNameAsync_WhenResourceExists_ExpectResourceAndCollectionsReturned(DbContextOptions options) { var resource = CreateApiResourceTestResource(); using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.ApiResources.Add(resource.ToEntity()); context.SaveChanges(); } ApiResource foundResource; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); foundResource = (await store.FindApiResourcesByNameAsync(new[] { resource.Name })).SingleOrDefault(); } Assert.NotNull(foundResource); Assert.True(foundResource.Name == resource.Name); Assert.NotNull(foundResource.UserClaims); Assert.NotEmpty(foundResource.UserClaims); Assert.NotNull(foundResource.ApiSecrets); Assert.NotEmpty(foundResource.ApiSecrets); Assert.NotNull(foundResource.Scopes); Assert.NotEmpty(foundResource.Scopes); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindApiResourcesByNameAsync_WhenResourcesExist_ExpectOnlyResourcesRequestedReturned(DbContextOptions options) { var resource = CreateApiResourceTestResource(); using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.ApiResources.Add(resource.ToEntity()); context.ApiResources.Add(CreateApiResourceTestResource().ToEntity()); context.SaveChanges(); } ApiResource foundResource; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); foundResource = (await store.FindApiResourcesByNameAsync(new[] { resource.Name })).SingleOrDefault(); } Assert.NotNull(foundResource); Assert.True(foundResource.Name == resource.Name); Assert.NotNull(foundResource.UserClaims); Assert.NotEmpty(foundResource.UserClaims); Assert.NotNull(foundResource.ApiSecrets); Assert.NotEmpty(foundResource.ApiSecrets); Assert.NotNull(foundResource.Scopes); Assert.NotEmpty(foundResource.Scopes); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindApiResourcesByScopeNameAsync_WhenResourcesExist_ExpectResourcesReturned(DbContextOptions options) { var testApiResource = CreateApiResourceTestResource(); var testApiScope = CreateApiScopeTestResource(); testApiResource.Scopes.Add(testApiScope.Name); using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.ApiResources.Add(testApiResource.ToEntity()); context.ApiScopes.Add(testApiScope.ToEntity()); context.SaveChanges(); } IEnumerable resources; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); resources = await store.FindApiResourcesByScopeNameAsync(new List { testApiScope.Name }); } Assert.NotNull(resources); Assert.NotEmpty(resources); Assert.NotNull(resources.Single(x => x.Name == testApiResource.Name)); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindApiResourcesByScopeNameAsync_WhenResourcesExist_ExpectOnlyResourcesRequestedReturned(DbContextOptions options) { var testIdentityResource = CreateIdentityTestResource(); var testApiResource = CreateApiResourceTestResource(); var testApiScope = CreateApiScopeTestResource(); testApiResource.Scopes.Add(testApiScope.Name); using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.IdentityResources.Add(testIdentityResource.ToEntity()); context.ApiResources.Add(testApiResource.ToEntity()); context.ApiScopes.Add(testApiScope.ToEntity()); context.IdentityResources.Add(CreateIdentityTestResource().ToEntity()); context.ApiResources.Add(CreateApiResourceTestResource().ToEntity()); context.ApiScopes.Add(CreateApiScopeTestResource().ToEntity()); context.SaveChanges(); } IEnumerable resources; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); resources = await store.FindApiResourcesByScopeNameAsync(new[] { testApiScope.Name }); } Assert.NotNull(resources); Assert.NotEmpty(resources); Assert.NotNull(resources.Single(x => x.Name == testApiResource.Name)); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindIdentityResourcesByScopeNameAsync_WhenResourceExists_ExpectResourceAndCollectionsReturned(DbContextOptions options) { var resource = CreateIdentityTestResource(); using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.IdentityResources.Add(resource.ToEntity()); context.SaveChanges(); } IList resources; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); resources = (await store.FindIdentityResourcesByScopeNameAsync(new List { resource.Name })).ToList(); } Assert.NotNull(resources); Assert.NotEmpty(resources); var foundScope = resources.Single(); Assert.Equal(resource.Name, foundScope.Name); Assert.NotNull(foundScope.UserClaims); Assert.NotEmpty(foundScope.UserClaims); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindIdentityResourcesByScopeNameAsync_WhenResourcesExist_ExpectOnlyRequestedReturned(DbContextOptions options) { var resource = CreateIdentityTestResource(); using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.IdentityResources.Add(resource.ToEntity()); context.IdentityResources.Add(CreateIdentityTestResource().ToEntity()); context.SaveChanges(); } IList resources; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); resources = (await store.FindIdentityResourcesByScopeNameAsync(new List { resource.Name })).ToList(); } Assert.NotNull(resources); Assert.NotEmpty(resources); Assert.NotNull(resources.Single(x => x.Name == resource.Name)); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindApiScopesByNameAsync_WhenResourceExists_ExpectResourceAndCollectionsReturned(DbContextOptions options) { var resource = CreateApiScopeTestResource(); using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.ApiScopes.Add(resource.ToEntity()); context.SaveChanges(); } IList resources; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); resources = (await store.FindApiScopesByNameAsync(new List { resource.Name })).ToList(); } Assert.NotNull(resources); Assert.NotEmpty(resources); var foundScope = resources.Single(); Assert.Equal(resource.Name, foundScope.Name); Assert.NotNull(foundScope.UserClaims); Assert.NotEmpty(foundScope.UserClaims); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task FindApiScopesByNameAsync_WhenResourcesExist_ExpectOnlyRequestedReturned(DbContextOptions options) { var resource = CreateApiScopeTestResource(); using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.ApiScopes.Add(resource.ToEntity()); context.ApiScopes.Add(CreateApiScopeTestResource().ToEntity()); context.SaveChanges(); } IList resources; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); resources = (await store.FindApiScopesByNameAsync(new List { resource.Name })).ToList(); } Assert.NotNull(resources); Assert.NotEmpty(resources); Assert.NotNull(resources.Single(x => x.Name == resource.Name)); } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task GetAllResources_WhenAllResourcesRequested_ExpectAllResourcesIncludingHidden(DbContextOptions options) { var visibleIdentityResource = CreateIdentityTestResource(); var visibleApiResource = CreateApiResourceTestResource(); var visibleApiScope = CreateApiScopeTestResource(); var hiddenIdentityResource = new IdentityResource { Name = Guid.NewGuid().ToString(), ShowInDiscoveryDocument = false }; var hiddenApiResource = new ApiResource { Name = Guid.NewGuid().ToString(), Scopes = { Guid.NewGuid().ToString() }, ShowInDiscoveryDocument = false }; var hiddenApiScope = new ApiScope { Name = Guid.NewGuid().ToString(), ShowInDiscoveryDocument = false }; using (var context = new ConfigurationDbContext(options, StoreOptions)) { context.IdentityResources.Add(visibleIdentityResource.ToEntity()); context.ApiResources.Add(visibleApiResource.ToEntity()); context.ApiScopes.Add(visibleApiScope.ToEntity()); context.IdentityResources.Add(hiddenIdentityResource.ToEntity()); context.ApiResources.Add(hiddenApiResource.ToEntity()); context.ApiScopes.Add(hiddenApiScope.ToEntity()); context.SaveChanges(); } Resources resources; using (var context = new ConfigurationDbContext(options, StoreOptions)) { var store = new ResourceStore(context, FakeLogger.Create()); resources = await store.GetAllResourcesAsync(); } Assert.NotNull(resources); Assert.NotEmpty(resources.IdentityResources); Assert.NotEmpty(resources.ApiResources); Assert.NotEmpty(resources.ApiScopes); Assert.Contains(resources.IdentityResources, x => x.Name == visibleIdentityResource.Name); Assert.Contains(resources.IdentityResources, x => x.Name == hiddenIdentityResource.Name); Assert.Contains(resources.ApiResources, x => x.Name == visibleApiResource.Name); Assert.Contains(resources.ApiResources, x => x.Name == hiddenApiResource.Name); Assert.Contains(resources.ApiScopes, x => x.Name == visibleApiScope.Name); Assert.Contains(resources.ApiScopes, x => x.Name == hiddenApiScope.Name); } } ================================================ FILE: src/EntityFramework.Storage/test/IntegrationTests/TokenCleanup/TokenCleanupTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer8.EntityFramework.DbContexts; using IdentityServer8.EntityFramework.Entities; using IdentityServer8.EntityFramework.Interfaces; using IdentityServer8.EntityFramework.Options; using IdentityServer8.EntityFramework.Stores; using IdentityServer8.Stores; using IdentityServer8.Test; using Microsoft.EntityFrameworkCore; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer8.EntityFramework.IntegrationTests.TokenCleanup; public class TokenCleanupTests : IntegrationTest { public TokenCleanupTests(DatabaseProviderFixture fixture) : base(fixture) { foreach (var options in TestDatabaseProviders.SelectMany(x => x.Select(y => (DbContextOptions)y)).ToList()) { using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.Database.EnsureCreated(); } } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveExpiredGrantsAsync_WhenExpiredGrantsExist_ExpectExpiredGrantsRemoved(DbContextOptions options) { var expiredGrant = new PersistedGrant { Key = Guid.NewGuid().ToString(), ClientId = "app1", Type = "reference", SubjectId = "123", Expiration = DateTime.UtcNow.AddDays(-3), Data = "{!}" }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(expiredGrant); context.SaveChanges(); } await CreateSut(options).RemoveExpiredGrantsAsync(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.FirstOrDefault(x => x.Key == expiredGrant.Key).Should().BeNull(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveExpiredGrantsAsync_WhenValidGrantsExist_ExpectValidGrantsInDb(DbContextOptions options) { var validGrant = new PersistedGrant { Key = Guid.NewGuid().ToString(), ClientId = "app1", Type = "reference", SubjectId = "123", Expiration = DateTime.UtcNow.AddDays(3), Data = "{!}" }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.Add(validGrant); context.SaveChanges(); } await CreateSut(options).RemoveExpiredGrantsAsync(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.PersistedGrants.FirstOrDefault(x => x.Key == validGrant.Key).Should().NotBeNull(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveExpiredGrantsAsync_WhenExpiredDeviceGrantsExist_ExpectExpiredDeviceGrantsRemoved(DbContextOptions options) { var expiredGrant = new DeviceFlowCodes { DeviceCode = Guid.NewGuid().ToString(), UserCode = Guid.NewGuid().ToString(), ClientId = "app1", SubjectId = "123", CreationTime = DateTime.UtcNow.AddDays(-4), Expiration = DateTime.UtcNow.AddDays(-3), Data = "{!}" }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.Add(expiredGrant); context.SaveChanges(); } await CreateSut(options).RemoveExpiredGrantsAsync(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.FirstOrDefault(x => x.DeviceCode == expiredGrant.DeviceCode).Should().BeNull(); } } [Theory, MemberData(nameof(TestDatabaseProviders))] public async Task RemoveExpiredGrantsAsync_WhenValidDeviceGrantsExist_ExpectValidDeviceGrantsInDb(DbContextOptions options) { var validGrant = new DeviceFlowCodes { DeviceCode = Guid.NewGuid().ToString(), UserCode = "2468", ClientId = "app1", SubjectId = "123", CreationTime = DateTime.UtcNow.AddDays(-4), Expiration = DateTime.UtcNow.AddDays(3), Data = "{!}" }; using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.Add(validGrant); context.SaveChanges(); } await CreateSut(options).RemoveExpiredGrantsAsync(); using (var context = new PersistedGrantDbContext(options, StoreOptions)) { context.DeviceFlowCodes.FirstOrDefault(x => x.DeviceCode == validGrant.DeviceCode).Should().NotBeNull(); } } private EntityFramework.TokenCleanupService CreateSut(DbContextOptions options) { IServiceCollection services = new ServiceCollection(); services.AddIdentityServer() .AddTestUsers(new List()) .AddInMemoryClients(new List()) .AddInMemoryIdentityResources(new List()) .AddInMemoryApiResources(new List()); services.AddScoped(_ => new PersistedGrantDbContext(options, StoreOptions)); services.AddTransient(); services.AddTransient(); services.AddTransient(); services.AddSingleton(StoreOptions); return services.BuildServiceProvider().GetRequiredService(); //return new EntityFramework.TokenCleanupService( // services.BuildServiceProvider(), // new NullLogger(), // StoreOptions); } } ================================================ FILE: src/EntityFramework.Storage/test/UnitTests/IdentityServer8.EntityFramework.UnitTests.csproj ================================================ ../../../../key.snk true true runtime; build; native; contentfiles; analyzers; buildtransitive all ================================================ FILE: src/EntityFramework.Storage/test/UnitTests/Mappers/ApiResourceMappersTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Linq; using FluentAssertions; using IdentityServer8.EntityFramework.Mappers; using Xunit; using ApiResource = IdentityServer8.Models.ApiResource; namespace IdentityServer8.EntityFramework.UnitTests.Mappers; public class ApiResourceMappersTests { [Fact] public void AutomapperConfigurationIsValid() { ApiResourceMappers.Mapper.ConfigurationProvider.AssertConfigurationIsValid(); } [Fact] public void Can_Map() { var model = new ApiResource(); var mappedEntity = model.ToEntity(); var mappedModel = mappedEntity.ToModel(); Assert.NotNull(mappedModel); Assert.NotNull(mappedEntity); } [Fact] public void Properties_Map() { var model = new ApiResource() { Description = "description", DisplayName = "displayname", Name = "foo", Scopes = { "foo1", "foo2" }, Enabled = false }; var mappedEntity = model.ToEntity(); mappedEntity.Scopes.Count.Should().Be(2); var foo1 = mappedEntity.Scopes.FirstOrDefault(x => x.Scope == "foo1"); foo1.Should().NotBeNull(); var foo2 = mappedEntity.Scopes.FirstOrDefault(x => x.Scope == "foo2"); foo2.Should().NotBeNull(); var mappedModel = mappedEntity.ToModel(); mappedModel.Description.Should().Be("description"); mappedModel.DisplayName.Should().Be("displayname"); mappedModel.Enabled.Should().BeFalse(); mappedModel.Name.Should().Be("foo"); } [Fact] public void missing_values_should_use_defaults() { var entity = new IdentityServer8.EntityFramework.Entities.ApiResource { Secrets = new System.Collections.Generic.List { new Entities.ApiResourceSecret { } } }; var def = new ApiResource { ApiSecrets = { new Models.Secret("foo") } }; var model = entity.ToModel(); model.ApiSecrets.First().Type.Should().Be(def.ApiSecrets.First().Type); } } ================================================ FILE: src/EntityFramework.Storage/test/UnitTests/Mappers/ClientMappersTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Linq; using FluentAssertions; using IdentityServer8.EntityFramework.Mappers; using Xunit; using Client = IdentityServer8.Models.Client; namespace IdentityServer8.EntityFramework.UnitTests.Mappers; public class ClientMappersTests { [Fact] public void AutomapperConfigurationIsValid() { ClientMappers.Mapper.ConfigurationProvider.AssertConfigurationIsValid(); } [Fact] public void Can_Map() { var model = new Client(); var mappedEntity = model.ToEntity(); var mappedModel = mappedEntity.ToModel(); Assert.NotNull(mappedModel); Assert.NotNull(mappedEntity); } [Fact] public void Properties_Map() { var model = new Client() { Properties = { {"foo1", "bar1"}, {"foo2", "bar2"}, } }; var mappedEntity = model.ToEntity(); mappedEntity.Properties.Count.Should().Be(2); var foo1 = mappedEntity.Properties.FirstOrDefault(x => x.Key == "foo1"); foo1.Should().NotBeNull(); foo1.Value.Should().Be("bar1"); var foo2 = mappedEntity.Properties.FirstOrDefault(x => x.Key == "foo2"); foo2.Should().NotBeNull(); foo2.Value.Should().Be("bar2"); var mappedModel = mappedEntity.ToModel(); mappedModel.Properties.Count.Should().Be(2); mappedModel.Properties.ContainsKey("foo1").Should().BeTrue(); mappedModel.Properties.ContainsKey("foo2").Should().BeTrue(); mappedModel.Properties["foo1"].Should().Be("bar1"); mappedModel.Properties["foo2"].Should().Be("bar2"); } [Fact] public void duplicates_properties_in_db_map() { var entity = new IdentityServer8.EntityFramework.Entities.Client { Properties = new System.Collections.Generic.List() { new Entities.ClientProperty{Key = "foo1", Value = "bar1"}, new Entities.ClientProperty{Key = "foo1", Value = "bar2"}, } }; Action modelAction = () => entity.ToModel(); modelAction.Should().Throw(); } [Fact] public void missing_values_should_use_defaults() { var entity = new IdentityServer8.EntityFramework.Entities.Client { ClientSecrets = new System.Collections.Generic.List { new Entities.ClientSecret { } } }; var def = new Client { ClientSecrets = { new Models.Secret("foo") } }; var model = entity.ToModel(); model.ProtocolType.Should().Be(def.ProtocolType); model.ClientSecrets.First().Type.Should().Be(def.ClientSecrets.First().Type); } } ================================================ FILE: src/EntityFramework.Storage/test/UnitTests/Mappers/IdentityResourcesMappersTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.EntityFramework.Mappers; using IdentityServer8.Models; using Xunit; namespace IdentityServer8.EntityFramework.UnitTests.Mappers; public class IdentityResourcesMappersTests { [Fact] public void IdentityResourceAutomapperConfigurationIsValid() { IdentityResourceMappers.Mapper.ConfigurationProvider.AssertConfigurationIsValid(); } [Fact] public void CanMapIdentityResources() { var model = new IdentityResource(); var mappedEntity = model.ToEntity(); var mappedModel = mappedEntity.ToModel(); Assert.NotNull(mappedModel); Assert.NotNull(mappedEntity); } } ================================================ FILE: src/EntityFramework.Storage/test/UnitTests/Mappers/PersistedGrantMappersTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer8.EntityFramework.Mappers; using IdentityServer8.Models; using Xunit; namespace IdentityServer8.EntityFramework.UnitTests.Mappers; public class PersistedGrantMappersTests { [Fact] public void PersistedGrantAutomapperConfigurationIsValid() { PersistedGrantMappers.Mapper.ConfigurationProvider.AssertConfigurationIsValid(); } [Fact] public void CanMap() { var model = new PersistedGrant() { ConsumedTime = new System.DateTime(2020, 02, 03, 4, 5, 6) }; var mappedEntity = model.ToEntity(); mappedEntity.ConsumedTime.Value.Should().Be(new System.DateTime(2020, 02, 03, 4, 5, 6)); var mappedModel = mappedEntity.ToModel(); mappedModel.ConsumedTime.Value.Should().Be(new System.DateTime(2020, 02, 03, 4, 5, 6)); Assert.NotNull(mappedModel); Assert.NotNull(mappedEntity); } } ================================================ FILE: src/EntityFramework.Storage/test/UnitTests/Mappers/ScopeMappersTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Linq; using FluentAssertions; using IdentityServer8.EntityFramework.Mappers; using IdentityServer8.Models; using Xunit; namespace IdentityServer8.EntityFramework.UnitTests.Mappers; public class ScopesMappersTests { [Fact] public void ScopeAutomapperConfigurationIsValid() { ScopeMappers.Mapper.ConfigurationProvider.AssertConfigurationIsValid(); } [Fact] public void CanMapScope() { var model = new ApiScope(); var mappedEntity = model.ToEntity(); var mappedModel = mappedEntity.ToModel(); Assert.NotNull(mappedModel); Assert.NotNull(mappedEntity); } [Fact] public void Properties_Map() { var model = new ApiScope() { Description = "description", DisplayName = "displayname", Name = "foo", UserClaims = { "c1", "c2" }, Properties = { { "x", "xx" }, { "y", "yy" }, }, Enabled = false }; var mappedEntity = model.ToEntity(); mappedEntity.Description.Should().Be("description"); mappedEntity.DisplayName.Should().Be("displayname"); mappedEntity.Name.Should().Be("foo"); mappedEntity.UserClaims.Count.Should().Be(2); mappedEntity.UserClaims.Select(x => x.Type).Should().BeEquivalentTo(new[] { "c1", "c2" }); mappedEntity.Properties.Count.Should().Be(2); mappedEntity.Properties.Should().Contain(x => x.Key == "x" && x.Value == "xx"); mappedEntity.Properties.Should().Contain(x => x.Key == "y" && x.Value == "yy"); var mappedModel = mappedEntity.ToModel(); mappedModel.Description.Should().Be("description"); mappedModel.DisplayName.Should().Be("displayname"); mappedModel.Enabled.Should().BeFalse(); mappedModel.Name.Should().Be("foo"); mappedModel.UserClaims.Count.Should().Be(2); mappedModel.UserClaims.Should().BeEquivalentTo(new[] { "c1", "c2" }); mappedModel.Properties.Count.Should().Be(2); mappedModel.Properties["x"].Should().Be("xx"); mappedModel.Properties["y"].Should().Be("yy"); } } ================================================ FILE: src/IdentityServer8/Directory.Build.props ================================================ ================================================ FILE: src/IdentityServer8/IdentityServer8.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34322.80 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5461C61B-B06E-46BA-B206-925B660BE727}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{45C22EDD-91B1-4AEF-8620-77F4E3E7C544}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8", "src\IdentityServer8.csproj", "{407C030E-60E6-41F7-AF43-0AC48EDCC17D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "host\Host.csproj", "{784B3C88-30FA-415D-B99E-584063064508}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer.UnitTests", "test\IdentityServer.UnitTests\IdentityServer.UnitTests.csproj", "{4291820C-735F-4776-8BC4-6527433BC683}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer.IntegrationTests", "test\IdentityServer.IntegrationTests\IdentityServer.IntegrationTests.csproj", "{94501373-478A-478D-8C17-6AC52E3438CF}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{9CB027A4-3509-4E54-B9D7-AC8F2F648AA2}" ProjectSection(SolutionItems) = preProject ..\..\.editorconfig = ..\..\.editorconfig ..\..\.gitattributes = ..\..\.gitattributes ..\..\.gitignore = ..\..\.gitignore ..\..\Directory.Build.props = ..\..\Directory.Build.props ..\..\Directory.Build.targets = ..\..\Directory.Build.targets ..\..\Directory.Packages.props = ..\..\Directory.Packages.props ..\..\global.json = ..\..\global.json ..\..\IdentityServer8.DotNet.ruleset = ..\..\IdentityServer8.DotNet.ruleset ..\..\LICENSE = ..\..\LICENSE ..\..\NuGet.config = ..\..\NuGet.config ..\..\README.md = ..\..\README.md ..\..\SECURITY.MD = ..\..\SECURITY.MD ..\..\SPONSORS.md = ..\..\SPONSORS.md EndProjectSection EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {407C030E-60E6-41F7-AF43-0AC48EDCC17D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {407C030E-60E6-41F7-AF43-0AC48EDCC17D}.Debug|Any CPU.Build.0 = Debug|Any CPU {407C030E-60E6-41F7-AF43-0AC48EDCC17D}.Release|Any CPU.ActiveCfg = Release|Any CPU {407C030E-60E6-41F7-AF43-0AC48EDCC17D}.Release|Any CPU.Build.0 = Release|Any CPU {784B3C88-30FA-415D-B99E-584063064508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {784B3C88-30FA-415D-B99E-584063064508}.Debug|Any CPU.Build.0 = Debug|Any CPU {784B3C88-30FA-415D-B99E-584063064508}.Release|Any CPU.ActiveCfg = Release|Any CPU {784B3C88-30FA-415D-B99E-584063064508}.Release|Any CPU.Build.0 = Release|Any CPU {4291820C-735F-4776-8BC4-6527433BC683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4291820C-735F-4776-8BC4-6527433BC683}.Debug|Any CPU.Build.0 = Debug|Any CPU {4291820C-735F-4776-8BC4-6527433BC683}.Release|Any CPU.ActiveCfg = Release|Any CPU {4291820C-735F-4776-8BC4-6527433BC683}.Release|Any CPU.Build.0 = Release|Any CPU {94501373-478A-478D-8C17-6AC52E3438CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {94501373-478A-478D-8C17-6AC52E3438CF}.Debug|Any CPU.Build.0 = Debug|Any CPU {94501373-478A-478D-8C17-6AC52E3438CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {94501373-478A-478D-8C17-6AC52E3438CF}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {407C030E-60E6-41F7-AF43-0AC48EDCC17D} = {5461C61B-B06E-46BA-B206-925B660BE727} {784B3C88-30FA-415D-B99E-584063064508} = {5461C61B-B06E-46BA-B206-925B660BE727} {4291820C-735F-4776-8BC4-6527433BC683} = {45C22EDD-91B1-4AEF-8620-77F4E3E7C544} {94501373-478A-478D-8C17-6AC52E3438CF} = {45C22EDD-91B1-4AEF-8620-77F4E3E7C544} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {176723F7-4A9B-4F05-A9F3-3BA715E4BDC3} EndGlobalSection EndGlobal ================================================ FILE: src/IdentityServer8/build.cmd ================================================ @echo off dotnet run --project build -- %* ================================================ FILE: src/IdentityServer8/build.ps1 ================================================ $ErrorActionPreference = "Stop"; dotnet run --project build -- $args ================================================ FILE: src/IdentityServer8/build.sh ================================================ #!/usr/bin/env bash set -euo pipefail dotnet run --project build -- "$@" ================================================ FILE: src/IdentityServer8/host/Configuration/Clients.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Configuration; public static class Clients { public static IEnumerable Get() { var clients = new List(); clients.AddRange(ClientsConsole.Get()); clients.AddRange(ClientsWeb.Get()); return clients; } } ================================================ FILE: src/IdentityServer8/host/Configuration/ClientsConsole.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Configuration; public static class ClientsConsole { public static IEnumerable Get() { return new List { /////////////////////////////////////////// // Console Client Credentials Flow Sample ////////////////////////////////////////// new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1", IdentityServerConstants.LocalApi.ScopeName } }, /////////////////////////////////////////// // Console Structured Scope Sample ////////////////////////////////////////// new Client { ClientId = "parameterized.client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "transaction" } }, /////////////////////////////////////////// // X509 mTLS Client ////////////////////////////////////////// new Client { ClientId = "mtls", ClientSecrets = { // new Secret(@"CN=mtls.test, OU=ROO\ballen@roo, O=mkcert development certificate", "mtls.test") // { // Type = SecretTypes.X509CertificateName // }, new Secret("5D9E9B6B333CD42C99D1DE6175CC0F3EF99DDF68", "mtls.test") { Type = IdentityServerConstants.SecretTypes.X509CertificateThumbprint }, }, AccessTokenType = AccessTokenType.Jwt, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console Client Credentials Flow with client JWT assertion ////////////////////////////////////////// new Client { ClientId = "client.jwt", ClientSecrets = { new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = "MIIEgTCCAumgAwIBAgIQDMMu7l/umJhfEbzJMpcttzANBgkqhkiG9w0BAQsFADCBkzEeMBwGA1UEChMVbWtjZXJ0IGRldmVsb3BtZW50IENBMTQwMgYDVQQLDCtkb21pbmlja0Bkb21icDE2LmZyaXR6LmJveCAoRG9taW5pY2sgQmFpZXIpMTswOQYDVQQDDDJta2NlcnQgZG9taW5pY2tAZG9tYnAxNi5mcml0ei5ib3ggKERvbWluaWNrIEJhaWVyKTAeFw0xOTA2MDEwMDAwMDBaFw0zMDAxMDMxMjM0MDdaMHAxJzAlBgNVBAoTHm1rY2VydCBkZXZlbG9wbWVudCBjZXJ0aWZpY2F0ZTE0MDIGA1UECwwrZG9taW5pY2tAZG9tYnAxNi5mcml0ei5ib3ggKERvbWluaWNrIEJhaWVyKTEPMA0GA1UEAxMGY2xpZW50MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAvNtpipaS8k1zA6w0Aoy8U4l+8zM4jHhhblExf3PULrMR6RauxniTki8p+P8CsZT4V8A4qo+JwsgpLIHrVQrbt9DEhHfBKzxwHqt+GoHt7byTfTtp8A/5nLhYc/5CW4HiR194gVx5+HAlvt+BriMTb1czvTf+H20dj41yUPsN7nMdyRLF+uXapQYMLYnq2BJIDq83mqGwojHk7d+N6GwoO95jlyas7KSoj8/FvfbaqkRNx0446hqPOzFHKc8er8K5VrLp6tVjh8ZJyY0F0dKgx6yWITsL54ctbj/cCyfuGjWEMbS2XXgc+x/xQMnmpfhK1qQAUn9jg5EzF9n6mQomOwIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYBBQUHAwIGCCsGAQUFBwMBMAwGA1UdEwEB/wQCMAAwHwYDVR0jBBgwFoAUEMUlw41YsKZQVls3pEG6CrJk4O8wEQYDVR0RBAowCIIGY2xpZW50MA0GCSqGSIb3DQEBCwUAA4IBgQC0TjNY4Q3Wmw7ggamDImV6HUng3WbYGLYbbL2e3myBrjIxGd1Bi8ZyOu8qeUMIRAbZt2YsSX5S8kx0biaVg2zC+aO5eHhEWMwKB66huInXFjI4wtxZ22r+33fg1R0cLuEUePhftOWrbL0MS4YXVyn9HUMWO4WptG9PJdxNw1UbEB8nw3FkVOdAC9RGqiqalSK+E2UT/kUbTIQ1gPSdQ3nh52mre0H/T9+IRqiozJtNK/CQg4NuEV7rUXHnp7Fmigp6RIJ4TCozglspL341y0rV8M7npU1FYZC2UKNr4ed+GOO1n/sF3LbXDlPXwne99CVVn85wjDaevoR7Md0y2KwE9EggLYcViXNehx4YVv/BjfgqxW8NxiKAxP6kPOZE0XdBrZj2rmcDcGOXCzzYpcduKhFyTOpA0K5RNGC3j1KOUjPVlOtLvjASP7udBEYNfH3mgqXAgqNDOEKi2jG9LITv2IyGUsXhTAsKNJ6A6qiDBzDrvPAYDvsfabPq6tRTwjA=" }, new Secret { Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = "{'e':'AQAB','kid':'ZzAjSnraU3bkWGnnAqLapYGpTyNfLbjbzgAPbbW2GEA','kty':'RSA','n':'wWwQFtSzeRjjerpEM5Rmqz_DsNaZ9S1Bw6UbZkDLowuuTCjBWUax0vBMMxdy6XjEEK4Oq9lKMvx9JzjmeJf1knoqSNrox3Ka0rnxXpNAz6sATvme8p9mTXyp0cX4lF4U2J54xa2_S9NF5QWvpXvBeC4GAJx7QaSw4zrUkrc6XyaAiFnLhQEwKJCwUw4NOqIuYvYp_IXhw-5Ti_icDlZS-282PcccnBeOcX7vc21pozibIdmZJKqXNsL1Ibx5Nkx1F1jLnekJAmdaACDjYRLL_6n3W4wUp19UvzB1lGtXcJKLLkqB6YDiZNu16OSiSprfmrRXvYmvD8m6Fnl5aetgKw'}" } }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Custom Grant Sample ////////////////////////////////////////// new Client { ClientId = "client.custom", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = { "custom", "custom.nosubject" }, AllowedScopes = { "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console Resource Owner Flow Sample ////////////////////////////////////////// new Client { ClientId = "roclient", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, "custom.profile", "resource1.scope1", "resource2.scope1" }, RefreshTokenUsage = TokenUsage.OneTimeOnly, AbsoluteRefreshTokenLifetime = 3600 * 24, SlidingRefreshTokenLifetime = 10, RefreshTokenExpiration = TokenExpiration.Sliding }, /////////////////////////////////////////// // Console Public Resource Owner Flow Sample ////////////////////////////////////////// new Client { ClientId = "roclient.public", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Console with PKCE Sample ////////////////////////////////////////// new Client { ClientId = "console.pkce", ClientName = "Console with PKCE Sample", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RedirectUris = { "http://127.0.0.1" }, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // WinConsole with PKCE Sample ////////////////////////////////////////// new Client { ClientId = "winconsole", ClientName = "Windows Console with PKCE Sample", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, RedirectUris = { "sample-windows-client://callback" }, RequireConsent = false, AllowOfflineAccess = true, AllowedIdentityTokenSigningAlgorithms = { "ES256" }, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } }, /////////////////////////////////////////// // Introspection Client Sample ////////////////////////////////////////// new Client { ClientId = "roclient.reference", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "resource1.scope1", "resource2.scope1", "scope3" }, AccessTokenType = AccessTokenType.Reference }, /////////////////////////////////////////// // Device Flow Sample ////////////////////////////////////////// new Client { ClientId = "device", ClientName = "Device Flow Client", AllowedGrantTypes = GrantTypes.DeviceFlow, RequireClientSecret = false, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1" } } }; } } ================================================ FILE: src/IdentityServer8/host/Configuration/ClientsWeb.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Configuration; public static class ClientsWeb { static string[] allowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Profile, IdentityServerConstants.StandardScopes.Email, "resource1.scope1", "resource2.scope1", "transaction" }; public static IEnumerable Get() { return new List { /////////////////////////////////////////// // JS OIDC Sample ////////////////////////////////////////// new Client { ClientId = "js_oidc", ClientName = "JavaScript OIDC Client", ClientUri = "http://identityserver8.io", AllowedGrantTypes = GrantTypes.Code, RequireClientSecret = false, RedirectUris = { "https://localhost:44300/index.html", "https://localhost:44300/callback.html", "https://localhost:44300/silent.html", "https://localhost:44300/popup.html" }, PostLogoutRedirectUris = { "https://localhost:44300/index.html" }, AllowedCorsOrigins = { "https://localhost:44300" }, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Automatic Token Management Sample ////////////////////////////////////////// new Client { ClientId = "mvc.tokenmanagement", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AccessTokenLifetime = 75, RedirectUris = { "https://localhost:44301/signin-oidc" }, FrontChannelLogoutUri = "https://localhost:44301/signout-oidc", PostLogoutRedirectUris = { "https://localhost:44301/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Code Flow Sample ////////////////////////////////////////// new Client { ClientId = "mvc.code", ClientName = "MVC Code Flow", ClientUri = "http://identityserver8.io", ClientSecrets = { new Secret("secret".Sha256()) }, RequireConsent = true, AllowedGrantTypes = GrantTypes.Code, RedirectUris = { "https://localhost:44302/signin-oidc" }, FrontChannelLogoutUri = "https://localhost:44302/signout-oidc", PostLogoutRedirectUris = { "https://localhost:44302/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes }, /////////////////////////////////////////// // MVC Hybrid Flow Sample (Back Channel logout) ////////////////////////////////////////// new Client { ClientId = "mvc.hybrid.backchannel", ClientName = "MVC Hybrid (with BackChannel logout)", ClientUri = "http://identityserver8.io", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Hybrid, RequirePkce = false, RedirectUris = { "https://localhost:44303/signin-oidc" }, BackChannelLogoutUri = "https://localhost:44303/logout", PostLogoutRedirectUris = { "https://localhost:44303/signout-callback-oidc" }, AllowOfflineAccess = true, AllowedScopes = allowedScopes } }; } } ================================================ FILE: src/IdentityServer8/host/Configuration/Resources.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Configuration; public class Resources { // identity resources represent identity data about a user that can be requested via the scope parameter (OpenID Connect) public static readonly IEnumerable IdentityResources = new[] { // some standard scopes from the OIDC spec new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email(), // custom identity resource with some consolidated claims new IdentityResource("custom.profile", new[] { JwtClaimTypes.Name, JwtClaimTypes.Email, "location", JwtClaimTypes.Address }) }; // API scopes represent values that describe scope of access and can be requested by the scope parameter (OAuth) public static readonly IEnumerable ApiScopes = new[] { // local API scope new ApiScope(LocalApi.ScopeName), // resource specific scopes new ApiScope("resource1.scope1"), new ApiScope("resource2.scope1"), // a scope without resource association new ApiScope("scope3"), // a scope shared by multiple resources new ApiScope("shared.scope"), // a parameterized scope new ApiScope("transaction", "Transaction") { Description = "Some Transaction" } }; // API resources are more formal representation of a resource with processing rules and their scopes (if any) public static readonly IEnumerable ApiResources = new[] { new ApiResource("resource1", "Resource 1") { ApiSecrets = { new Secret("secret".Sha256()) }, Scopes = { "resource1.scope1", "shared.scope" } }, new ApiResource("resource2", "Resource 2") { ApiSecrets = { new Secret("secret".Sha256()) }, // additional claims to put into access token UserClaims = { JwtClaimTypes.Name, JwtClaimTypes.Email }, Scopes = { "resource2.scope1", "shared.scope" } } }; } ================================================ FILE: src/IdentityServer8/host/Extensions/ExtensionGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Extensions; public class ExtensionGrantValidator : IExtensionGrantValidator { public Task ValidateAsync(ExtensionGrantValidationContext context) { var credential = context.Request.Raw.Get("custom_credential"); if (credential != null) { context.Result = new GrantValidationResult(subject: "818727", authenticationMethod: "custom"); } else { // custom error message context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential"); } return Task.CompletedTask; } public string GrantType { get { return "custom"; } } } ================================================ FILE: src/IdentityServer8/host/Extensions/HostProfileService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Extensions; public class HostProfileService : TestUserProfileService { public HostProfileService(TestUserStore users, ILogger logger) : base(users, logger) { } public override async Task GetProfileDataAsync(ProfileDataRequestContext context) { await base.GetProfileDataAsync(context); var transaction = context.RequestedResources.ParsedScopes.FirstOrDefault(x => x.ParsedName == "transaction"); if (transaction?.ParsedParameter != null) { context.IssuedClaims.Add(new Claim("transaction_id", transaction.ParsedParameter)); } } } ================================================ FILE: src/IdentityServer8/host/Extensions/NoSubjectExtensionGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Extensions; public class NoSubjectExtensionGrantValidator : IExtensionGrantValidator { public Task ValidateAsync(ExtensionGrantValidationContext context) { var credential = context.Request.Raw.Get("custom_credential"); if (credential != null) { context.Result = new GrantValidationResult(); } else { // custom error message context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential"); } return Task.CompletedTask; } public string GrantType { get { return "custom.nosubject"; } } } ================================================ FILE: src/IdentityServer8/host/Extensions/ParameterizedScopeParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Extensions; public class ParameterizedScopeParser : DefaultScopeParser { public ParameterizedScopeParser(ILogger logger) : base(logger) { } public override void ParseScopeValue(ParseScopeContext scopeContext) { const string transactionScopeName = "transaction"; const string separator = ":"; const string transactionScopePrefix = transactionScopeName + separator; var scopeValue = scopeContext.RawValue; if (scopeValue.StartsWith(transactionScopePrefix)) { // we get in here with a scope like "transaction:something" var parts = scopeValue.Split(separator, StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 2) { scopeContext.SetParsedValues(transactionScopeName, parts[1]); } else { scopeContext.SetError("transaction scope missing transaction parameter value"); } } else if (scopeValue != transactionScopeName) { // we get in here with a scope not like "transaction" base.ParseScopeValue(scopeContext); } else { // we get in here with a scope exactly "transaction", which is to say we're ignoring it // and not including it in the results scopeContext.SetIgnore(); } } } ================================================ FILE: src/IdentityServer8/host/Extensions/ParameterizedScopeTokenRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Extensions; public class ParameterizedScopeTokenRequestValidator : ICustomTokenRequestValidator { public Task ValidateAsync(CustomTokenRequestValidationContext context) { var transaction = context.Result.ValidatedRequest.ValidatedResources.ParsedScopes.FirstOrDefault(x => x.ParsedName == "transaction"); if (transaction?.ParsedParameter != null) { context.Result.ValidatedRequest.ClientClaims.Add(new Claim(transaction.ParsedName, transaction.ParsedParameter)); } return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/host/Extensions/SameSiteHandlingExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Extensions; // copied from https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/ public static class SameSiteHandlingExtensions { public static IServiceCollection AddSameSiteCookiePolicy(this IServiceCollection services) { services.Configure(options => { options.MinimumSameSitePolicy = SameSiteMode.Unspecified; options.OnAppendCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); options.OnDeleteCookie = cookieContext => CheckSameSite(cookieContext.Context, cookieContext.CookieOptions); }); return services; } private static void CheckSameSite(HttpContext httpContext, CookieOptions options) { if (options.SameSite == SameSiteMode.None) { var userAgent = httpContext.Request.Headers["User-Agent"].ToString(); if (!httpContext.Request.IsHttps || DisallowsSameSiteNone(userAgent)) { // For .NET Core < 3.1 set SameSite = (SameSiteMode)(-1) options.SameSite = SameSiteMode.Unspecified; } } } private static bool DisallowsSameSiteNone(string userAgent) { // Cover all iOS based browsers here. This includes: // - Safari on iOS 12 for iPhone, iPod Touch, iPad // - WkWebview on iOS 12 for iPhone, iPod Touch, iPad // - Chrome on iOS 12 for iPhone, iPod Touch, iPad // All of which are broken by SameSite=None, because they use the iOS networking stack if (userAgent.Contains("CPU iPhone OS 12") || userAgent.Contains("iPad; CPU OS 12")) { return true; } // Cover Mac OS X based browsers that use the Mac OS networking stack. This includes: // - Safari on Mac OS X. // This does not include: // - Chrome on Mac OS X // Because they do not use the Mac OS networking stack. if (userAgent.Contains("Macintosh; Intel Mac OS X 10_14") && userAgent.Contains("Version/") && userAgent.Contains("Safari")) { return true; } // Cover Chrome 50-69, because some versions are broken by SameSite=None, // and none in this range require it. // Note: this covers some pre-Chromium Edge versions, // but pre-Chromium Edge does not require SameSite=None. if (userAgent.Contains("Chrome/5") || userAgent.Contains("Chrome/6")) { return true; } return false; } } ================================================ FILE: src/IdentityServer8/host/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Services; global using IdentityServer8.Stores; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using IdentityServerHost.Configuration; global using IdentityServerHost.Extensions; global using IdentityServerHost.Quickstart.UI; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authentication.Certificate; global using Microsoft.AspNetCore.Authorization; global using Microsoft.AspNetCore.HttpOverrides; global using Microsoft.AspNetCore.Mvc; global using Microsoft.AspNetCore.Mvc.Filters; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Options; global using Microsoft.IdentityModel.Logging; global using Microsoft.IdentityModel.Tokens; global using Newtonsoft.Json; global using Serilog; global using Serilog.Events; global using Serilog.Sinks.SystemConsole.Themes; global using System.ComponentModel.DataAnnotations; global using System.Diagnostics; global using System.Security.Claims; global using System.Security.Cryptography.X509Certificates; global using System.Text; global using static IdentityServer8.IdentityServerConstants; global using ILogger = Microsoft.Extensions.Logging.ILogger; global using ClaimValueTypes = System.Security.Claims.ClaimValueTypes; ================================================ FILE: src/IdentityServer8/host/Host.csproj ================================================ ================================================ FILE: src/IdentityServer8/host/LocalApiController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost; [Route("localApi")] [Authorize(LocalApi.PolicyName)] public class LocalApiController : ControllerBase { public IActionResult Get() { var claims = from c in User.Claims select new { c.Type, c.Value }; return new JsonResult(claims); } } ================================================ FILE: src/IdentityServer8/host/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost; public class Program { public static int Main(string[] args) { Console.Title = "IdentityServer8"; Activity.DefaultIdFormat = ActivityIdFormat.W3C; Log.Logger = new LoggerConfiguration() .MinimumLevel.Debug() .MinimumLevel.Override("Microsoft", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information) .MinimumLevel.Override("System", LogEventLevel.Warning) .MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information) .Enrich.FromLogContext() //.WriteTo.File(@"IdentityServer8_log.txt") // uncomment to write to Azure diagnostics stream //.WriteTo.File( // @"D:\home\LogFiles\Application\identityserver.txt", // fileSizeLimitBytes: 1_000_000, // rollOnFileSizeLimit: true, // shared: true, // flushToDiskInterval: TimeSpan.FromSeconds(1)) .WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code) .CreateLogger(); try { Log.Information("Starting host..."); CreateHostBuilder(args).Build().Run(); return 0; } catch (Exception ex) { Log.Fatal(ex, "Host terminated unexpectedly."); return 1; } finally { Log.CloseAndFlush(); } } public static IHostBuilder CreateHostBuilder(string[] args) => Host.CreateDefaultBuilder(args) .UseSerilog() .ConfigureWebHostDefaults(webBuilder => { webBuilder.UseStartup(); }); } ================================================ FILE: src/IdentityServer8/host/Properties/launchSettings.json ================================================ { "profiles": { "Host": { "commandName": "Project", "launchBrowser": true, "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001;http://localhost:5000" }, "Host (proxy)": { "commandName": "Project", "launchBrowser": true, "launchUrl": "https://identityserver.local", "environmentVariables": { "ASPNETCORE_ENVIRONMENT": "Development" }, "applicationUrl": "https://localhost:5001;http://localhost:5000" } } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/AccountController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller implements a typical login/logout/provision workflow for local and external accounts. /// The login service encapsulates the interactions with the user data store. This data store is in-memory only and cannot be used for production! /// The interaction service provides a way for the UI to communicate with identityserver for validation and context retrieval /// [SecurityHeaders] [AllowAnonymous] public class AccountController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly IAuthenticationSchemeProvider _schemeProvider; private readonly IEventService _events; public AccountController( IIdentityServerInteractionService interaction, IClientStore clientStore, IAuthenticationSchemeProvider schemeProvider, IEventService events, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _schemeProvider = schemeProvider; _events = events; } /// /// Entry point into the login workflow /// [HttpGet] public async Task Login(string returnUrl) { // build a model so we know what to show on the login page var vm = await BuildLoginViewModelAsync(returnUrl); if (vm.IsExternalLoginOnly) { // we only have one option for logging in and it's an external provider return returnUrl.IsAllowedRedirect() ? RedirectToAction("Challenge", "External", new { scheme = vm.ExternalLoginScheme, returnUrl = returnUrl.SanitizeForRedirect() }) : Forbid(); } return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task Login(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (ModelState.IsValid) { // validate username/password against in-memory store if (_users.ValidateCredentials(model.Username, model.Password)) { var user = _users.FindByUsername(model.Username); await _events.RaiseAsync(new UserLoginSuccessEvent(user.Username, user.SubjectId, user.Username, clientId: context?.Client.ClientId)); // only set explicit expiration here if user chooses "remember me". // otherwise we rely upon expiration configured in cookie middleware. AuthenticationProperties props = null; if (AccountOptions.AllowRememberLogin && model.RememberLogin) { props = new AuthenticationProperties { IsPersistent = true, ExpiresUtc = DateTimeOffset.UtcNow.Add(AccountOptions.RememberMeLoginDuration) }; }; // issue authentication cookie with subject ID and username var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username }; await HttpContext.SignInAsync(isuser, props); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } // request for a local page if (Url.IsLocalUrl(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else if (string.IsNullOrEmpty(model.ReturnUrl)) { return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } else { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } } await _events.RaiseAsync(new UserLoginFailureEvent(model.Username, "invalid credentials", clientId: context?.Client.ClientId)); ModelState.AddModelError(string.Empty, AccountOptions.InvalidCredentialsErrorMessage); } // something went wrong, show form with error var vm = await BuildLoginViewModelAsync(model); return View(vm); } /// /// Handle postback from username/password login /// [HttpPost] [ValidateAntiForgeryToken] public async Task LoginCancel(LoginInputModel model) { // check if we are in the context of an authorization request var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context != null) { // if the user cancels, send a result back into IdentityServer as if they // denied the consent (even if this client does not require consent). // this will send back an access denied OIDC error response to the client. await _interaction.DenyAuthorizationAsync(context, AuthorizationError.AccessDenied); // we can trust model.ReturnUrl since GetAuthorizationContextAsync returned non-null if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return model.ReturnUrl.IsAllowedRedirect() ? this.LoadingPage("Redirect", model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } return model.ReturnUrl.IsAllowedRedirect() ? Redirect(model.ReturnUrl.SanitizeForRedirect()) : Forbid(); } else { // since we don't have a valid context, then we just go back to the home page return model.ReturnUrl.IsAllowedRedirect() ? Redirect("~/") : Forbid(); } } /// /// Show logout page /// [HttpGet] public async Task Logout(string logoutId) { // build a model so the logout page knows what to display var vm = await BuildLogoutViewModelAsync(logoutId); if (vm.ShowLogoutPrompt == false) { // if the request for logout was properly authenticated from IdentityServer, then // we don't need to show the prompt and can just log the user out directly. return await Logout(vm); } return View(vm); } /// /// Handle logout page postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Logout(LogoutInputModel model) { // build a model so the logged out page knows what to display var vm = await BuildLoggedOutViewModelAsync(model.LogoutId); if (User?.Identity.IsAuthenticated == true) { // delete local authentication cookie await HttpContext.SignOutAsync(); // raise the logout event await _events.RaiseAsync(new UserLogoutSuccessEvent(User.GetSubjectId(), User.GetDisplayName())); } // check if we need to trigger sign-out at an upstream identity provider if (vm.TriggerExternalSignout) { // build a return URL so the upstream provider will redirect back // to us after the user has logged out. this allows us to then // complete our single sign-out processing. string url = Url.Action("Logout", new { logoutId = vm.LogoutId }); // this triggers a redirect to the external provider for sign-out return SignOut(new AuthenticationProperties { RedirectUri = url }, vm.ExternalAuthenticationScheme); } return View("LoggedOut", vm); } [HttpGet] public IActionResult AccessDenied() { return View(); } /*****************************************/ /* helper APIs for the AccountController */ /*****************************************/ private async Task BuildLoginViewModelAsync(string returnUrl) { var context = await _interaction.GetAuthorizationContextAsync(returnUrl); if (context?.IdP != null && await _schemeProvider.GetSchemeAsync(context.IdP) != null) { var local = context.IdP == IdentityServer8.IdentityServerConstants.LocalIdentityProvider; // this is meant to short circuit the UI and only trigger the one external IdP var vm = new LoginViewModel { EnableLocalLogin = local, ReturnUrl = returnUrl, Username = context?.LoginHint, }; if (!local) { vm.ExternalProviders = new[] { new ExternalProvider { AuthenticationScheme = context.IdP } }; } return vm; } var schemes = await _schemeProvider.GetAllSchemesAsync(); var providers = schemes .Where(x => x.DisplayName != null) .Select(x => new ExternalProvider { DisplayName = x.DisplayName ?? x.Name, AuthenticationScheme = x.Name }).ToList(); var allowLocal = true; if (context?.Client.ClientId != null) { var client = await _clientStore.FindEnabledClientByIdAsync(context.Client.ClientId); if (client != null) { allowLocal = client.EnableLocalLogin; if (client.IdentityProviderRestrictions != null && client.IdentityProviderRestrictions.Any()) { providers = providers.Where(provider => client.IdentityProviderRestrictions.Contains(provider.AuthenticationScheme)).ToList(); } } } return new LoginViewModel { AllowRememberLogin = AccountOptions.AllowRememberLogin, EnableLocalLogin = allowLocal && AccountOptions.AllowLocalLogin, ReturnUrl = returnUrl, Username = context?.LoginHint, ExternalProviders = providers.ToArray() }; } private async Task BuildLoginViewModelAsync(LoginInputModel model) { var vm = await BuildLoginViewModelAsync(model.ReturnUrl); vm.Username = model.Username; vm.RememberLogin = model.RememberLogin; return vm; } private async Task BuildLogoutViewModelAsync(string logoutId) { var vm = new LogoutViewModel { LogoutId = logoutId, ShowLogoutPrompt = AccountOptions.ShowLogoutPrompt }; if (User?.Identity.IsAuthenticated != true) { // if the user is not authenticated, then just show logged out page vm.ShowLogoutPrompt = false; return vm; } var context = await _interaction.GetLogoutContextAsync(logoutId); if (context?.ShowSignoutPrompt == false) { // it's safe to automatically sign-out vm.ShowLogoutPrompt = false; return vm; } // show the logout prompt. this prevents attacks where the user // is automatically signed out by another malicious web page. return vm; } private async Task BuildLoggedOutViewModelAsync(string logoutId) { // get context information (client name, post logout redirect URI and iframe for federated signout) var logout = await _interaction.GetLogoutContextAsync(logoutId); var vm = new LoggedOutViewModel { AutomaticRedirectAfterSignOut = AccountOptions.AutomaticRedirectAfterSignOut, PostLogoutRedirectUri = logout?.PostLogoutRedirectUri, ClientName = string.IsNullOrEmpty(logout?.ClientName) ? logout?.ClientId : logout?.ClientName, SignOutIframeUrl = logout?.SignOutIFrameUrl, LogoutId = logoutId }; if (User?.Identity.IsAuthenticated == true) { var idp = User.FindFirst(JwtClaimTypes.IdentityProvider)?.Value; if (idp != null && idp != IdentityServer8.IdentityServerConstants.LocalIdentityProvider) { var providerSupportsSignout = await HttpContext.GetSchemeSupportsSignOutAsync(idp); if (providerSupportsSignout) { if (vm.LogoutId == null) { // if there's no current logout context, we need to create one // this captures necessary info from the current logged in user // before we signout and redirect away to the external IdP for signout vm.LogoutId = await _interaction.CreateLogoutContextAsync(); } vm.ExternalAuthenticationScheme = idp; } } } return vm; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/AccountOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class AccountOptions { public static bool AllowLocalLogin = true; public static bool AllowRememberLogin = true; public static TimeSpan RememberMeLoginDuration = TimeSpan.FromDays(30); public static bool ShowLogoutPrompt = true; public static bool AutomaticRedirectAfterSignOut = false; public static string InvalidCredentialsErrorMessage = "Invalid username or password"; } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/ExternalController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class ExternalController : Controller { private readonly TestUserStore _users; private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clientStore; private readonly ILogger _logger; private readonly IEventService _events; public ExternalController( IIdentityServerInteractionService interaction, IClientStore clientStore, IEventService events, ILogger logger, TestUserStore users = null) { // if the TestUserStore is not in DI, then we'll just use the global users collection // this is where you would plug in your own custom identity management library (e.g. ASP.NET Identity) _users = users ?? new TestUserStore(TestUsers.Users); _interaction = interaction; _clientStore = clientStore; _logger = logger; _events = events; } /// /// initiate roundtrip to external authentication provider /// [HttpGet] public IActionResult Challenge(string scheme, string returnUrl) { if (string.IsNullOrEmpty(returnUrl)) returnUrl = "~/"; // validate returnUrl - either it is a valid OIDC URL or back to a local page if (Url.IsLocalUrl(returnUrl) == false && _interaction.IsValidReturnUrl(returnUrl) == false) { // user might have clicked on a malicious link - should be logged throw new Exception("invalid return URL"); } // start challenge and roundtrip the return URL and scheme var props = new AuthenticationProperties { RedirectUri = Url.Action(nameof(Callback)), Items = { { "returnUrl", returnUrl }, { "scheme", scheme }, } }; return Challenge(props, scheme); } /// /// Post processing of external authentication /// [HttpGet] public async Task Callback() { // read external identity from the temporary cookie var result = await HttpContext.AuthenticateAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); if (result?.Succeeded != true) { throw new Exception("External authentication error"); } if (_logger.IsEnabled(LogLevel.Debug)) { var externalClaims = result.Principal.Claims.Select(c => $"{c.Type}: {c.Value}"); _logger.LogDebug("External claims: {@claims}", externalClaims); } // lookup our user and external provider info var (user, provider, providerUserId, claims) = FindUserFromExternalProvider(result); if (user == null) { // this might be where you might initiate a custom workflow for user registration // in this sample we don't show how that would be done, as our sample implementation // simply auto-provisions new external user user = AutoProvisionUser(provider, providerUserId, claims); } // this allows us to collect any additional claims or properties // for the specific protocols used and store them in the local auth cookie. // this is typically used to store data needed for signout from those protocols. var additionalLocalClaims = new List(); var localSignInProps = new AuthenticationProperties(); ProcessLoginCallback(result, additionalLocalClaims, localSignInProps); // issue authentication cookie for user var isuser = new IdentityServerUser(user.SubjectId) { DisplayName = user.Username, IdentityProvider = provider, AdditionalClaims = additionalLocalClaims }; await HttpContext.SignInAsync(isuser, localSignInProps); // delete temporary cookie used during external authentication await HttpContext.SignOutAsync(IdentityServerConstants.ExternalCookieAuthenticationScheme); // retrieve return URL var returnUrl = result.Properties.Items["returnUrl"] ?? "~/"; // check if external login is in the context of an OIDC request var context = await _interaction.GetAuthorizationContextAsync(returnUrl); await _events.RaiseAsync(new UserLoginSuccessEvent(provider, providerUserId, user.SubjectId, user.Username, true, context?.Client.ClientId)); if (context != null) { if (context.IsNativeClient()) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", returnUrl); } } return Redirect(returnUrl); } private (TestUser user, string provider, string providerUserId, IEnumerable claims) FindUserFromExternalProvider(AuthenticateResult result) { var externalUser = result.Principal; // try to determine the unique id of the external user (issued by the provider) // the most common claim type for that are the sub claim and the NameIdentifier // depending on the external provider, some other claim type might be used var userIdClaim = externalUser.FindFirst(JwtClaimTypes.Subject) ?? externalUser.FindFirst(ClaimTypes.NameIdentifier) ?? throw new Exception("Unknown userid"); // remove the user id claim so we don't include it as an extra claim if/when we provision the user var claims = externalUser.Claims.ToList(); claims.Remove(userIdClaim); var provider = result.Properties.Items["scheme"]; var providerUserId = userIdClaim.Value; // find external user var user = _users.FindByExternalProvider(provider, providerUserId); return (user, provider, providerUserId, claims); } private TestUser AutoProvisionUser(string provider, string providerUserId, IEnumerable claims) { var user = _users.AutoProvisionUser(provider, providerUserId, claims.ToList()); return user; } // if the external login is OIDC-based, there are certain things we need to preserve to make logout work // this will be different for WS-Fed, SAML2p or other protocols private void ProcessLoginCallback(AuthenticateResult externalResult, List localClaims, AuthenticationProperties localSignInProps) { // if the external system sent a session id claim, copy it over // so we can use it for single sign-out var sid = externalResult.Principal.Claims.FirstOrDefault(x => x.Type == JwtClaimTypes.SessionId); if (sid != null) { localClaims.Add(new Claim(JwtClaimTypes.SessionId, sid.Value)); } // if the external provider issued an id_token, we'll keep it for signout var idToken = externalResult.Properties.GetTokenValue("id_token"); if (idToken != null) { localSignInProps.StoreTokens(new[] { new AuthenticationToken { Name = "id_token", Value = idToken } }); } } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/ExternalProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ExternalProvider { public string DisplayName { get; set; } public string AuthenticationScheme { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/LoggedOutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoggedOutViewModel { public string PostLogoutRedirectUri { get; set; } public string ClientName { get; set; } public string SignOutIframeUrl { get; set; } public bool AutomaticRedirectAfterSignOut { get; set; } public string LogoutId { get; set; } public bool TriggerExternalSignout => ExternalAuthenticationScheme != null; public string ExternalAuthenticationScheme { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/LoginInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginInputModel { [Required] public string Username { get; set; } [Required] public string Password { get; set; } public bool RememberLogin { get; set; } public string ReturnUrl { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/LoginViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LoginViewModel : LoginInputModel { public bool AllowRememberLogin { get; set; } = true; public bool EnableLocalLogin { get; set; } = true; public IEnumerable ExternalProviders { get; set; } = Enumerable.Empty(); public IEnumerable VisibleExternalProviders => ExternalProviders.Where(x => !String.IsNullOrWhiteSpace(x.DisplayName)); public bool IsExternalLoginOnly => EnableLocalLogin == false && ExternalProviders?.Count() == 1; public string ExternalLoginScheme => IsExternalLoginOnly ? ExternalProviders?.SingleOrDefault()?.AuthenticationScheme : null; } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/LogoutInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutInputModel { public string LogoutId { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/LogoutViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class LogoutViewModel : LogoutInputModel { public bool ShowLogoutPrompt { get; set; } = true; } ================================================ FILE: src/IdentityServer8/host/Quickstart/Account/RedirectViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class RedirectViewModel { public string RedirectUrl { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Consent/ConsentController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This controller processes the consent UI /// [SecurityHeaders] [Authorize] public class ConsentController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IEventService _events; private readonly ILogger _logger; public ConsentController( IIdentityServerInteractionService interaction, IEventService events, ILogger logger) { _interaction = interaction; _events = events; _logger = logger; } /// /// Shows the consent screen /// /// /// [HttpGet] public async Task Index(string returnUrl) { var vm = await BuildViewModelAsync(returnUrl); if (vm != null) { return View("Index", vm); } return View("Error"); } /// /// Handles the consent screen postback /// [HttpPost] [ValidateAntiForgeryToken] public async Task Index(ConsentInputModel model) { var result = await ProcessConsent(model); if (result.IsRedirect) { var context = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (context?.IsNativeClient() == true) { // The client is native, so this change in how to // return the response is for better UX for the end user. return this.LoadingPage("Redirect", result.RedirectUri); } return result.RedirectUri.IsAllowedRedirect() ? Redirect(result.RedirectUri.SanitizeForRedirect()) : Forbid(); } if (result.HasValidationError) { ModelState.AddModelError(string.Empty, result.ValidationError); } if (result.ShowView) { return View("Index", result.ViewModel); } return View("Error"); } /*****************************************/ /* helper APIs for the ConsentController */ /*****************************************/ private async Task ProcessConsent(ConsentInputModel model) { var result = new ProcessConsentResult(); // validate return url is still valid var request = await _interaction.GetAuthorizationContextAsync(model.ReturnUrl); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model?.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model?.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.GrantConsentAsync(request, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.ReturnUrl, model); } return result; } private async Task BuildViewModelAsync(string returnUrl, ConsentInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(returnUrl); if (request != null) { return CreateConsentViewModel(model, returnUrl, request); } else { _logger.LogError("No consent request matching request: {0}", returnUrl.SanitizeForLog()); } return null; } private ConsentViewModel CreateConsentViewModel( ConsentInputModel model, string returnUrl, AuthorizationRequest request) { var vm = new ConsentViewModel { RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), Description = model?.Description, ReturnUrl = returnUrl, ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach(var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { var displayName = apiScope.DisplayName ?? apiScope.Name; if (!String.IsNullOrWhiteSpace(parsedScopeValue.ParsedParameter)) { displayName += ":" + parsedScopeValue.ParsedParameter; } return new ScopeViewModel { Value = parsedScopeValue.RawValue, DisplayName = displayName, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Consent/ConsentInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentInputModel { public string Button { get; set; } public IEnumerable ScopesConsented { get; set; } public bool RememberConsent { get; set; } public string ReturnUrl { get; set; } public string Description { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Consent/ConsentOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentOptions { public static bool EnableOfflineAccess = true; public static string OfflineAccessDisplayName = "Offline Access"; public static string OfflineAccessDescription = "Access to your applications and resources, even when you are offline"; public static readonly string MustChooseOneErrorMessage = "You must pick at least one permission"; public static readonly string InvalidSelectionErrorMessage = "Invalid selection"; } ================================================ FILE: src/IdentityServer8/host/Quickstart/Consent/ConsentViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ConsentViewModel : ConsentInputModel { public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public bool AllowRememberConsent { get; set; } public IEnumerable IdentityScopes { get; set; } public IEnumerable ApiScopes { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Consent/ProcessConsentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ProcessConsentResult { public bool IsRedirect => RedirectUri != null; public string RedirectUri { get; set; } public Client Client { get; set; } public bool ShowView => ViewModel != null; public ConsentViewModel ViewModel { get; set; } public bool HasValidationError => ValidationError != null; public string ValidationError { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Consent/ScopeViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ScopeViewModel { public string Value { get; set; } public string DisplayName { get; set; } public string Description { get; set; } public bool Emphasize { get; set; } public bool Required { get; set; } public bool Checked { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Device/DeviceAuthorizationInputModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationInputModel : ConsentInputModel { public string UserCode { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Device/DeviceAuthorizationViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DeviceAuthorizationViewModel : ConsentViewModel { public string UserCode { get; set; } public bool ConfirmUserCode { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Device/DeviceController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [Authorize] [SecurityHeaders] public class DeviceController : Controller { private readonly IDeviceFlowInteractionService _interaction; private readonly IEventService _events; private readonly IOptions _options; private readonly ILogger _logger; public DeviceController( IDeviceFlowInteractionService interaction, IEventService eventService, IOptions options, ILogger logger) { _interaction = interaction; _events = eventService; _options = options; _logger = logger; } [HttpGet] public async Task Index() { string userCodeParamName = _options.Value.UserInteraction.DeviceVerificationUserCodeParameter; string userCode = Request.Query[userCodeParamName]; if (string.IsNullOrWhiteSpace(userCode)) return View("UserCodeCapture"); var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); vm.ConfirmUserCode = true; return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task UserCodeCapture(string userCode) { var vm = await BuildViewModelAsync(userCode); if (vm == null) return View("Error"); return View("UserCodeConfirmation", vm); } [HttpPost] [ValidateAntiForgeryToken] public async Task Callback(DeviceAuthorizationInputModel model) { if (model == null) throw new ArgumentNullException(nameof(model)); var result = await ProcessConsent(model); if (result.HasValidationError) return View("Error"); return View("Success"); } private async Task ProcessConsent(DeviceAuthorizationInputModel model) { var result = new ProcessConsentResult(); var request = await _interaction.GetAuthorizationContextAsync(model.UserCode); if (request == null) return result; ConsentResponse grantedConsent = null; // user clicked 'no' - send back the standard 'access_denied' response if (model.Button == "no") { grantedConsent = new ConsentResponse { Error = AuthorizationError.AccessDenied }; // emit event await _events.RaiseAsync(new ConsentDeniedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues)); } // user clicked 'yes' - validate the data else if (model.Button == "yes") { // if the user consented to some scope, build the response model if (model.ScopesConsented != null && model.ScopesConsented.Any()) { var scopes = model.ScopesConsented; if (ConsentOptions.EnableOfflineAccess == false) { scopes = scopes.Where(x => x != IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess); } grantedConsent = new ConsentResponse { RememberConsent = model.RememberConsent, ScopesValuesConsented = scopes.ToArray(), Description = model.Description }; // emit event await _events.RaiseAsync(new ConsentGrantedEvent(User.GetSubjectId(), request.Client.ClientId, request.ValidatedResources.RawScopeValues, grantedConsent.ScopesValuesConsented, grantedConsent.RememberConsent)); } else { result.ValidationError = ConsentOptions.MustChooseOneErrorMessage; } } else { result.ValidationError = ConsentOptions.InvalidSelectionErrorMessage; } if (grantedConsent != null) { // communicate outcome of consent back to identityserver await _interaction.HandleRequestAsync(model.UserCode, grantedConsent); // indicate that's it ok to redirect back to authorization endpoint result.RedirectUri = model.ReturnUrl; result.Client = request.Client; } else { // we need to redisplay the consent UI result.ViewModel = await BuildViewModelAsync(model.UserCode, model); } return result; } private async Task BuildViewModelAsync(string userCode, DeviceAuthorizationInputModel model = null) { var request = await _interaction.GetAuthorizationContextAsync(userCode); if (request != null) { return CreateConsentViewModel(userCode, model, request); } return null; } private DeviceAuthorizationViewModel CreateConsentViewModel(string userCode, DeviceAuthorizationInputModel model, DeviceFlowAuthorizationRequest request) { var vm = new DeviceAuthorizationViewModel { UserCode = userCode, Description = model?.Description, RememberConsent = model?.RememberConsent ?? true, ScopesConsented = model?.ScopesConsented ?? Enumerable.Empty(), ClientName = request.Client.ClientName ?? request.Client.ClientId, ClientUrl = request.Client.ClientUri, ClientLogoUrl = request.Client.LogoUri, AllowRememberConsent = request.Client.AllowRememberConsent }; vm.IdentityScopes = request.ValidatedResources.Resources.IdentityResources.Select(x => CreateScopeViewModel(x, vm.ScopesConsented.Contains(x.Name) || model == null)).ToArray(); var apiScopes = new List(); foreach (var parsedScope in request.ValidatedResources.ParsedScopes) { var apiScope = request.ValidatedResources.Resources.FindApiScope(parsedScope.ParsedName); if (apiScope != null) { var scopeVm = CreateScopeViewModel(parsedScope, apiScope, vm.ScopesConsented.Contains(parsedScope.RawValue) || model == null); apiScopes.Add(scopeVm); } } if (ConsentOptions.EnableOfflineAccess && request.ValidatedResources.Resources.OfflineAccess) { apiScopes.Add(GetOfflineAccessScope(vm.ScopesConsented.Contains(IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess) || model == null)); } vm.ApiScopes = apiScopes; return vm; } private ScopeViewModel CreateScopeViewModel(IdentityResource identity, bool check) { return new ScopeViewModel { Value = identity.Name, DisplayName = identity.DisplayName ?? identity.Name, Description = identity.Description, Emphasize = identity.Emphasize, Required = identity.Required, Checked = check || identity.Required }; } public ScopeViewModel CreateScopeViewModel(ParsedScopeValue parsedScopeValue, ApiScope apiScope, bool check) { return new ScopeViewModel { Value = parsedScopeValue.RawValue, // todo: use the parsed scope value in the display? DisplayName = apiScope.DisplayName ?? apiScope.Name, Description = apiScope.Description, Emphasize = apiScope.Emphasize, Required = apiScope.Required, Checked = check || apiScope.Required }; } private ScopeViewModel GetOfflineAccessScope(bool check) { return new ScopeViewModel { Value = IdentityServer8.IdentityServerConstants.StandardScopes.OfflineAccess, DisplayName = ConsentOptions.OfflineAccessDisplayName, Description = ConsentOptions.OfflineAccessDescription, Emphasize = true, Checked = check }; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Diagnostics/DiagnosticsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [Authorize] public class DiagnosticsController : Controller { public async Task Index() { var localAddresses = new string[] { "127.0.0.1", "::1", HttpContext.Connection.LocalIpAddress.ToString() }; if (!localAddresses.Contains(HttpContext.Connection.RemoteIpAddress.ToString())) { return NotFound(); } var model = new DiagnosticsViewModel(await HttpContext.AuthenticateAsync()); return View(model); } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Diagnostics/DiagnosticsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class DiagnosticsViewModel { public DiagnosticsViewModel(AuthenticateResult result) { AuthenticateResult = result; if (result.Properties.Items.ContainsKey("client_list")) { var encoded = result.Properties.Items["client_list"]; var bytes = Base64Url.Decode(encoded); var value = Encoding.UTF8.GetString(bytes); Clients = JsonConvert.DeserializeObject(value); } } public AuthenticateResult AuthenticateResult { get; } public IEnumerable Clients { get; } = new List(); } ================================================ FILE: src/IdentityServer8/host/Quickstart/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public static class Extensions { /// /// Checks if the redirect URI is for a native client. /// /// public static bool IsNativeClient(this AuthorizationRequest context) { return !context.RedirectUri.StartsWith("https", StringComparison.Ordinal) && !context.RedirectUri.StartsWith("http", StringComparison.Ordinal); } public static IActionResult LoadingPage(this Controller controller, string viewName, string redirectUri) { controller.HttpContext.Response.StatusCode = 200; controller.HttpContext.Response.Headers["Location"] = ""; return controller.View(viewName, new RedirectViewModel { RedirectUrl = redirectUri }); } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Grants/GrantsController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; /// /// This sample controller allows a user to revoke grants given to clients /// [SecurityHeaders] [Authorize] public class GrantsController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IClientStore _clients; private readonly IResourceStore _resources; private readonly IEventService _events; public GrantsController(IIdentityServerInteractionService interaction, IClientStore clients, IResourceStore resources, IEventService events) { _interaction = interaction; _clients = clients; _resources = resources; _events = events; } /// /// Show list of grants /// [HttpGet] public async Task Index() { return View("Index", await BuildViewModelAsync()); } /// /// Handle postback to revoke a client /// [HttpPost] [ValidateAntiForgeryToken] public async Task Revoke(string clientId) { await _interaction.RevokeUserConsentAsync(clientId); await _events.RaiseAsync(new GrantsRevokedEvent(User.GetSubjectId(), clientId)); return RedirectToAction("Index"); } private async Task BuildViewModelAsync() { var grants = await _interaction.GetAllUserGrantsAsync(); var list = new List(); foreach(var grant in grants) { var client = await _clients.FindClientByIdAsync(grant.ClientId); if (client != null) { var resources = await _resources.FindResourcesByScopeAsync(grant.Scopes); var item = new GrantViewModel() { ClientId = client.ClientId, ClientName = client.ClientName ?? client.ClientId, ClientLogoUrl = client.LogoUri, ClientUrl = client.ClientUri, Description = grant.Description, Created = grant.CreationTime, Expires = grant.Expiration, IdentityGrantNames = resources.IdentityResources.Select(x => x.DisplayName ?? x.Name).ToArray(), ApiGrantNames = resources.ApiScopes.Select(x => x.DisplayName ?? x.Name).ToArray() }; list.Add(item); } } return new GrantsViewModel { Grants = list }; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Grants/GrantsViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class GrantsViewModel { public IEnumerable Grants { get; set; } } public class GrantViewModel { public string ClientId { get; set; } public string ClientName { get; set; } public string ClientUrl { get; set; } public string ClientLogoUrl { get; set; } public string Description { get; set; } public DateTime Created { get; set; } public DateTime? Expires { get; set; } public IEnumerable IdentityGrantNames { get; set; } public IEnumerable ApiGrantNames { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Home/ErrorViewModel.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class ErrorViewModel { public ErrorViewModel() { } public ErrorViewModel(string error) { Error = new ErrorMessage { Error = error }; } public ErrorMessage Error { get; set; } } ================================================ FILE: src/IdentityServer8/host/Quickstart/Home/HomeController.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; [SecurityHeaders] [AllowAnonymous] public class HomeController : Controller { private readonly IIdentityServerInteractionService _interaction; private readonly IWebHostEnvironment _environment; private readonly ILogger _logger; public HomeController(IIdentityServerInteractionService interaction, IWebHostEnvironment environment, ILogger logger) { _interaction = interaction; _environment = environment; _logger = logger; } public IActionResult Index() { if (_environment.IsDevelopment()) { // only show in development return View(); } _logger.LogInformation("Homepage is disabled in production. Returning 404."); return NotFound(); } /// /// Shows the error page /// public async Task Error(string errorId) { var vm = new ErrorViewModel(); // retrieve error details from identityserver var message = await _interaction.GetErrorContextAsync(errorId); if (message != null) { vm.Error = message; if (!_environment.IsDevelopment()) { // only show in development message.ErrorDescription = null; } } return View("Error", vm); } } ================================================ FILE: src/IdentityServer8/host/Quickstart/SecurityHeadersAttribute.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServerHost.Quickstart.UI; public class SecurityHeadersAttribute : ActionFilterAttribute { public override void OnResultExecuting(ResultExecutingContext context) { var result = context.Result; if (result is ViewResult) { // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Content-Type-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Type-Options")) { context.HttpContext.Response.Headers.Append("X-Content-Type-Options", "nosniff"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/X-Frame-Options if (!context.HttpContext.Response.Headers.ContainsKey("X-Frame-Options")) { context.HttpContext.Response.Headers.Append("X-Frame-Options", "SAMEORIGIN"); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy var csp = "default-src 'self'; object-src 'none'; frame-ancestors 'none'; sandbox allow-forms allow-same-origin allow-scripts; base-uri 'self';"; // also consider adding upgrade-insecure-requests once you have HTTPS in place for production //csp += "upgrade-insecure-requests;"; // also an example if you need client images to be displayed from twitter // csp += "img-src 'self' https://pbs.twimg.com;"; // once for standards compliant browsers if (!context.HttpContext.Response.Headers.ContainsKey("Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("Content-Security-Policy", csp); } // and once again for IE if (!context.HttpContext.Response.Headers.ContainsKey("X-Content-Security-Policy")) { context.HttpContext.Response.Headers.Append("X-Content-Security-Policy", csp); } // https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Referrer-Policy var referrer_policy = "no-referrer"; if (!context.HttpContext.Response.Headers.ContainsKey("Referrer-Policy")) { context.HttpContext.Response.Headers.Append("Referrer-Policy", referrer_policy); } } } } ================================================ FILE: src/IdentityServer8/host/Quickstart/TestUsers.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using JsonSerializer = System.Text.Json.JsonSerializer; namespace IdentityServerHost.Quickstart.UI; public class TestUsers { public static List Users { get { var address = new { street_address = "One Hacker Way", locality = "Heidelberg", postal_code = 69118, country = "Germany" }; return new List { new TestUser { SubjectId = "818727", Username = "alice", Password = "alice", Claims = { new Claim(JwtClaimTypes.Name, "Alice Smith"), new Claim(JwtClaimTypes.GivenName, "Alice"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://alice.com"), new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json) } }, new TestUser { SubjectId = "88421113", Username = "bob", Password = "bob", Claims = { new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim(JwtClaimTypes.Address, JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json) } } }; } } } ================================================ FILE: src/IdentityServer8/host/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Resources = IdentityServerHost.Configuration.Resources; using IdentityServerHost.Extensions; namespace IdentityServerHost; public class Startup { private readonly IConfiguration _config; public Startup(IConfiguration config) { _config = config; IdentityModelEventSource.ShowPII = true; } public void ConfigureServices(IServiceCollection services) { services.AddControllersWithViews(); // cookie policy to deal with temporary browser incompatibilities services.AddSameSiteCookiePolicy(); var builder = services.AddIdentityServer(options => { options.Events.RaiseSuccessEvents = true; options.Events.RaiseFailureEvents = true; options.Events.RaiseErrorEvents = true; options.Events.RaiseInformationEvents = true; options.EmitScopesAsSpaceDelimitedStringInJwt = true; options.MutualTls.Enabled = true; options.MutualTls.DomainName = "mtls"; //options.MutualTls.AlwaysEmitConfirmationClaim = true; }) .AddInMemoryClients(Clients.Get()) .AddInMemoryIdentityResources(Resources.IdentityResources) .AddInMemoryApiScopes(Resources.ApiScopes) .AddInMemoryApiResources(Resources.ApiResources) .AddSigningCredential() .AddExtensionGrantValidator() .AddExtensionGrantValidator() .AddJwtBearerClientAuthentication() .AddAppAuthRedirectUriValidator() .AddTestUsers(TestUsers.Users) .AddProfileService() .AddCustomTokenRequestValidator() .AddScopeParser() .AddMutualTlsSecretValidators(); // use this for persisted grants store // var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name; // const string connectionString = "DataSource=identityserver.db"; // builder.AddOperationalStore(options => // { // options.ConfigureDbContext = b => b.UseSqlite(connectionString, // sql => sql.MigrationsAssembly(migrationsAssembly)); // }); services.AddExternalIdentityProviders(); services.AddAuthentication() .AddCertificate(options => { options.AllowedCertificateTypes = CertificateTypes.All; options.RevocationMode = X509RevocationMode.NoCheck; }); services.AddCertificateForwardingForNginx(); services.AddLocalApiAuthentication(principal => { principal.Identities.First().AddClaim(new Claim("additional_claim", "additional_value")); return Task.FromResult(principal); }); } public void Configure(IApplicationBuilder app) { // use this for persisted grants store // app.InitializePersistedGrantsStore(); app.UseForwardedHeaders(new ForwardedHeadersOptions { ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto }); app.UseCertificateForwarding(); app.UseCookiePolicy(); app.UseSerilogRequestLogging(); app.UseDeveloperExceptionPage(); app.UseStaticFiles(); app.UseRouting(); app.UseIdentityServer(); app.UseAuthorization(); app.UseEndpoints(endpoints => { endpoints.MapDefaultControllerRoute(); }); } } public static class BuilderExtensions { public static IIdentityServerBuilder AddSigningCredential(this IIdentityServerBuilder builder) { // create random RS256 key //builder.AddDeveloperSigningCredential(); // use an RSA-based certificate with RS256 var rsaCert = new X509Certificate2("./keys/identityserver.test.rsa.p12", "changeit"); builder.AddSigningCredential(rsaCert, "RS256"); // ...and PS256 builder.AddSigningCredential(rsaCert, "PS256"); // or manually extract ECDSA key from certificate (directly using the certificate is not support by Microsoft right now) var ecCert = new X509Certificate2("./keys/identityserver.test.ecdsa.p12", "changeit"); var key = new ECDsaSecurityKey(ecCert.GetECDsaPrivateKey()) { KeyId = CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex) }; return builder.AddSigningCredential( key, IdentityServerConstants.ECDsaSigningAlgorithm.ES256); } // use this for persisted grants store // public static void InitializePersistedGrantsStore(this IApplicationBuilder app) // { // using (var serviceScope = app.ApplicationServices.GetService().CreateScope()) // { // serviceScope.ServiceProvider.GetRequiredService().Database.Migrate(); // } // } } public static class ServiceExtensions { public static IServiceCollection AddExternalIdentityProviders(this IServiceCollection services) { // configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache. services.AddOidcStateDataFormatterCache("aad", "demoidsrv"); services.AddAuthentication() .AddOpenIdConnect("Google", "Google", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.ForwardSignOut = IdentityServerConstants.DefaultCookieAuthenticationScheme; options.Authority = "https://accounts.google.com/"; options.ClientId = "708996912208-9m4dkjb5hscn7cjrn5u0r4tbgkbj1fko.apps.googleusercontent.com"; options.CallbackPath = "/signin-google"; options.Scope.Add("email"); }) .AddOpenIdConnect("demoidsrv", "IdentityServer", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.Authority = "https://demo.identityserver8.io/"; options.ClientId = "login"; options.ResponseType = "id_token"; options.SaveTokens = true; options.CallbackPath = "/signin-idsrv"; options.SignedOutCallbackPath = "/signout-callback-idsrv"; options.RemoteSignOutPath = "/signout-idsrv"; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; }) .AddOpenIdConnect("aad", "Azure AD", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.Authority = "https://login.windows.net/4ca9cb4c-5e5f-4be9-b700-c532992a3705"; options.ClientId = "96e3c53e-01cb-4244-b658-a42164cb67a9"; options.ResponseType = "id_token"; options.CallbackPath = "/signin-aad"; options.SignedOutCallbackPath = "/signout-callback-aad"; options.RemoteSignOutPath = "/signout-aad"; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; }) .AddOpenIdConnect("adfs", "ADFS", options => { options.SignInScheme = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.SignOutScheme = IdentityServerConstants.SignoutScheme; options.Authority = "https://adfs.leastprivilege.vm/adfs"; options.ClientId = "c0ea8d99-f1e7-43b0-a100-7dee3f2e5c3c"; options.ResponseType = "id_token"; options.CallbackPath = "/signin-adfs"; options.SignedOutCallbackPath = "/signout-callback-adfs"; options.RemoteSignOutPath = "/signout-adfs"; options.TokenValidationParameters = new TokenValidationParameters { NameClaimType = "name", RoleClaimType = "role" }; }); return services; } public static void AddCertificateForwardingForNginx(this IServiceCollection services) { services.AddCertificateForwarding(options => { options.CertificateHeader = "X-SSL-CERT"; options.HeaderConverter = (headerValue) => { X509Certificate2 clientCertificate = null; if(!string.IsNullOrWhiteSpace(headerValue)) { byte[] bytes = Encoding.UTF8.GetBytes(Uri.UnescapeDataString(headerValue)); clientCertificate = new X509Certificate2(bytes); } return clientCertificate; }; }); } } ================================================ FILE: src/IdentityServer8/host/Views/Account/AccessDenied.cshtml ================================================ 

    Access Denied

    You do not have access to that resource.

    ================================================ FILE: src/IdentityServer8/host/Views/Account/LoggedOut.cshtml ================================================ @model LoggedOutViewModel @{ // set this so the layout rendering sees an anonymous user ViewData["signed-out"] = true; }

    Logout You are now logged out

    @if (Model.PostLogoutRedirectUri != null) {
    Click here to return to the @Model.ClientName application.
    } @if (Model.SignOutIframeUrl != null) { }
    @section scripts { @if (Model.AutomaticRedirectAfterSignOut) { } } ================================================ FILE: src/IdentityServer8/host/Views/Account/Login.cshtml ================================================ @model LoginViewModel ================================================ FILE: src/IdentityServer8/host/Views/Account/Logout.cshtml ================================================ @model LogoutViewModel

    Logout

    Would you like to logout of IdentityServer?

    ================================================ FILE: src/IdentityServer8/host/Views/Consent/Index.cshtml ================================================ @model ConsentViewModel ================================================ FILE: src/IdentityServer8/host/Views/Device/Success.cshtml ================================================

    Success

    You have successfully authorized the device

    ================================================ FILE: src/IdentityServer8/host/Views/Device/UserCodeCapture.cshtml ================================================ @model string

    User Code

    Please enter the code displayed on your device.

    ================================================ FILE: src/IdentityServer8/host/Views/Device/UserCodeConfirmation.cshtml ================================================ @model DeviceAuthorizationViewModel
    @if (Model.ClientLogoUrl != null) { }

    @Model.ClientName is requesting your permission

    @if (Model.ConfirmUserCode) {

    Please confirm that the authorization request quotes the code: @Model.UserCode.

    }

    Uncheck the permissions you do not wish to grant.

    @if (Model.IdentityScopes.Any()) {
    Personal Information
      @foreach (var scope in Model.IdentityScopes) { }
    } @if (Model.ApiScopes.Any()) {
    Application Access
      @foreach (var scope in Model.ApiScopes) { }
    }
    Description
    @if (Model.AllowRememberConsent) {
    }
    @if (Model.ClientUrl != null) { @Model.ClientName }
    ================================================ FILE: src/IdentityServer8/host/Views/Diagnostics/Index.cshtml ================================================ @model DiagnosticsViewModel

    Authentication Cookie

    Claims

    @foreach (var claim in Model.AuthenticateResult.Principal.Claims) {
    @claim.Type
    @claim.Value
    }

    Properties

    @foreach (var prop in Model.AuthenticateResult.Properties.Items) {
    @prop.Key
    @prop.Value
    } @if (Model.Clients.Any()) {
    Clients
    @{ var clients = Model.Clients.ToArray(); for(var i = 0; i < clients.Length; i++) { @clients[i] if (i < clients.Length - 1) { , } } }
    }
    ================================================ FILE: src/IdentityServer8/host/Views/Grants/Index.cshtml ================================================ @model GrantsViewModel

    Client Application Permissions

    Below is the list of applications you have given permission to and the resources they have access to.

    @if (Model.Grants.Any() == false) {
    You have not given access to any applications
    } else { foreach (var grant in Model.Grants) {
    @if (grant.ClientLogoUrl != null) { } @grant.ClientName
      @if (grant.Description != null) {
    • @grant.Description
    • }
    • @grant.Created.ToString("yyyy-MM-dd")
    • @if (grant.Expires.HasValue) {
    • @grant.Expires.Value.ToString("yyyy-MM-dd")
    • } @if (grant.IdentityGrantNames.Any()) {
      • @foreach (var name in grant.IdentityGrantNames) {
      • @name
      • }
    • } @if (grant.ApiGrantNames.Any()) {
      • @foreach (var name in grant.ApiGrantNames) {
      • @name
      • }
    • }
    } }
    ================================================ FILE: src/IdentityServer8/host/Views/Home/Index.cshtml ================================================ @using System.Diagnostics @using System.Reflection @{ var version = typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.GetCustomAttribute()?.InformationalVersion.Split('+').First(); }

    Welcome to IdentityServer8 (version @version)

    ================================================ FILE: src/IdentityServer8/host/Views/Shared/Error.cshtml ================================================ @model ErrorViewModel @{ var error = Model?.Error?.Error; var errorDescription = Model?.Error?.ErrorDescription; var request_id = Model?.Error?.RequestId; }

    Error

    Sorry, there was an error @if (error != null) { : @error if (errorDescription != null) {
    @errorDescription
    } }
    @if (request_id != null) {
    Request Id: @request_id
    }
    ================================================ FILE: src/IdentityServer8/host/Views/Shared/Redirect.cshtml ================================================ @model RedirectViewModel @using Microsoft.Extensions.DependencyInjection;

    You are now being returned to the application

    Once complete, you may close this tab.

    ================================================ FILE: src/IdentityServer8/host/Views/Shared/_Layout.cshtml ================================================ IdentityServer8
    @RenderBody()
    @RenderSection("scripts", required: false) ================================================ FILE: src/IdentityServer8/host/Views/Shared/_Nav.cshtml ================================================ @using IdentityServer8.Extensions @{ string name = null; if (!true.Equals(ViewData["signed-out"])) { name = Context.User?.GetDisplayName(); } } ================================================ FILE: src/IdentityServer8/host/Views/Shared/_ScopeListItem.cshtml ================================================ @model ScopeViewModel
  • @if (Model.Required) { (required) } @if (Model.Description != null) { }
  • ================================================ FILE: src/IdentityServer8/host/Views/Shared/_ValidationSummary.cshtml ================================================ @if (ViewContext.ModelState.IsValid == false) {
    Error
    } ================================================ FILE: src/IdentityServer8/host/Views/_ViewImports.cshtml ================================================ @using IdentityServerHost.Quickstart.UI @addTagHelper *, Microsoft.AspNetCore.Mvc.TagHelpers ================================================ FILE: src/IdentityServer8/host/Views/_ViewStart.cshtml ================================================ @{ Layout = "_Layout"; } ================================================ FILE: src/IdentityServer8/host/appsettings.json ================================================ { "ApiScopes": [ { "Name": "IdentityServerApi" }, { "Name": "resource1.scope1" }, { "Name": "resource2.scope1" }, { "Name": "scope3" }, { "Name": "shared.scope" }, { "Name": "transaction", "DisplayName": "Transaction", "Description": "A transaction" } ], "ApiResources": [ { "Name": "resource1", "DisplayName": "Resource #1", "Scopes": [ "resource1.scope1", "shared.scope" ] }, { "Name": "resource2", "DisplayName": "Resource #2", "UserClaims": [ "name", "email" ], "Scopes": [ "resource2.scope1", "shared.scope" ] } ], "Clients": [ { "ClientId": "machine_client", "ClientSecrets": [ { "Value": "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=" } ], "AllowedGrantTypes": [ "client_credentials" ], "AllowedScopes": [ "resource1.scope1", "resource1.scope2" ], "Properties": { "foo": "bar" }, "Claims": [ { "type": "c1", "value": "c1value" }, { "type": "c2", "value": "c2value" } ] }, { "ClientId": "interactive_client", "ClientSecrets": [ { "Value": "K7gNU3sdo+OL0wNhqoVWhr3g6s1xYv72ol/pe/Unols=" } ], "AllowedGrantTypes": [ "authorization_code", "client_credentials" ], "AllowedScopes": [ "openid", "profile", "resource1.scope1", "resource1.scope2" ] } ] } ================================================ FILE: src/IdentityServer8/host/compilerconfig.json ================================================ [ { "outputFile": "wwwroot/css/site.css", "inputFile": "wwwroot/css/site.scss" } ] ================================================ FILE: src/IdentityServer8/host/compilerconfig.json.defaults ================================================ { "compilers": { "less": { "autoPrefix": "", "cssComb": "none", "ieCompat": true, "strictMath": false, "strictUnits": false, "relativeUrls": true, "rootPath": "", "sourceMapRoot": "", "sourceMapBasePath": "", "sourceMap": false }, "sass": { "autoPrefix": "", "includePath": "", "indentType": "space", "indentWidth": 2, "outputStyle": "nested", "Precision": 5, "relativeUrls": true, "sourceMapRoot": "", "lineFeed": "", "sourceMap": false }, "stylus": { "sourceMap": false }, "babel": { "sourceMap": false }, "coffeescript": { "bare": false, "runtimeMode": "node", "sourceMap": false }, "handlebars": { "root": "", "noBOM": false, "name": "", "namespace": "", "knownHelpersOnly": false, "forcePartial": false, "knownHelpers": [], "commonjs": "", "amd": false, "sourceMap": false } }, "minifiers": { "css": { "enabled": true, "termSemicolons": true, "gzip": false }, "javascript": { "enabled": true, "termSemicolons": true, "gzip": false } } } ================================================ FILE: src/IdentityServer8/host/libman.json ================================================ { "version": "1.0", "defaultProvider": "cdnjs", "libraries": [ { "provider": "jsdelivr", "library": "jquery@3.7.1", "destination": "wwwroot/lib/jquery/" }, { "provider": "jsdelivr", "library": "bootstrap@5.3.2", "destination": "wwwroot/lib/bootstrap/" }, { "provider": "jsdelivr", "library": "jquery-validation@1.20.0", "destination": "wwwroot/lib/jquery-validation/" }, { "provider": "jsdelivr", "library": "jquery-validation-unobtrusive@4.0.0", "destination": "wwwroot/lib/jquery-validation-unobtrusive/" } ] } ================================================ FILE: src/IdentityServer8/host/wwwroot/css/site.css ================================================ .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 lightgray; } .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; } ================================================ FILE: src/IdentityServer8/host/wwwroot/css/site.scss ================================================ .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 lightgray; .card-title { img { width: 100px; height: 100px; } font-size: 120%; font-weight: bold; } label { font-weight: bold; } } } ================================================ FILE: src/IdentityServer8/host/wwwroot/js/signin-redirect.js ================================================ //window.location.href = document.querySelector("meta[http-equiv=refresh]").getAttribute("data-url"); ================================================ FILE: src/IdentityServer8/host/wwwroot/js/signout-redirect.js ================================================ window.addEventListener("load", function () { var a = document.querySelector("a.PostLogoutRedirectUri"); if (a) { window.location = a.href; } }); ================================================ FILE: src/IdentityServer8/src/Configuration/CryptoHelper.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Configuration; /// /// Crypto helper /// public static class CryptoHelper { /// /// Creates a new RSA security key. /// /// public static RsaSecurityKey CreateRsaSecurityKey(int keySize = 2048) { return new RsaSecurityKey(RSA.Create(keySize)) { KeyId = CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex) }; } /// /// Creates a new ECDSA security key. /// /// The name of the curve as defined in /// https://tools.ietf.org/html/rfc7518#section-6.2.1.1. /// public static ECDsaSecurityKey CreateECDsaSecurityKey(string curve = JsonWebKeyECTypes.P256) { return new ECDsaSecurityKey(ECDsa.Create(GetCurveFromCrvValue(curve))) { KeyId = CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex) }; } /// /// Creates an RSA security key. /// /// The parameters. /// The identifier. /// public static RsaSecurityKey CreateRsaSecurityKey(RSAParameters parameters, string id) { var key = new RsaSecurityKey(parameters) { KeyId = id }; return key; } /// /// Creates the hash for the various hash claims (e.g. c_hash, at_hash or s_hash). /// /// The value to hash. /// The token signing algorithm /// public static string CreateHashClaimValue(string value, string tokenSigningAlgorithm) { using (var sha = GetHashAlgorithmForSigningAlgorithm(tokenSigningAlgorithm)) { var hash = sha.ComputeHash(Encoding.ASCII.GetBytes(value)); var size = (sha.HashSize / 8) / 2; var leftPart = new byte[size]; Array.Copy(hash, leftPart, size); return Base64Url.Encode(leftPart); } } /// /// Returns the matching hashing algorithm for a token signing algorithm /// /// The signing algorithm /// public static HashAlgorithm GetHashAlgorithmForSigningAlgorithm(string signingAlgorithm) { var signingAlgorithmBits = int.Parse(signingAlgorithm.Substring(signingAlgorithm.Length - 3)); return signingAlgorithmBits switch { 256 => SHA256.Create(), 384 => SHA384.Create(), 512 => SHA512.Create(), _ => throw new InvalidOperationException($"Invalid signing algorithm: {signingAlgorithm}"), }; } /// /// Returns the matching named curve for RFC 7518 crv value /// internal static ECCurve GetCurveFromCrvValue(string crv) { return crv switch { JsonWebKeyECTypes.P256 => ECCurve.NamedCurves.nistP256, JsonWebKeyECTypes.P384 => ECCurve.NamedCurves.nistP384, JsonWebKeyECTypes.P521 => ECCurve.NamedCurves.nistP521, _ => throw new InvalidOperationException($"Unsupported curve type of {crv}"), }; } /// /// Return the matching RFC 7518 crv value for curve /// internal static string GetCrvValueFromCurve(ECCurve curve) { return curve.Oid.Value switch { Constants.CurveOids.P256 => JsonWebKeyECTypes.P256, Constants.CurveOids.P384 => JsonWebKeyECTypes.P384, Constants.CurveOids.P521 => JsonWebKeyECTypes.P521, _ => throw new InvalidOperationException($"Unsupported curve type of {curve.Oid.Value} - {curve.Oid.FriendlyName}"), }; } internal static bool IsValidCurveForAlgorithm(ECDsaSecurityKey key, string algorithm) { var parameters = key.ECDsa.ExportParameters(false); if (algorithm == SecurityAlgorithms.EcdsaSha256 && parameters.Curve.Oid.Value != Constants.CurveOids.P256 || algorithm == SecurityAlgorithms.EcdsaSha384 && parameters.Curve.Oid.Value != Constants.CurveOids.P384 || algorithm == SecurityAlgorithms.EcdsaSha512 && parameters.Curve.Oid.Value != Constants.CurveOids.P521) { return false; } return true; } internal static bool IsValidCrvValueForAlgorithm(string crv) { return crv == JsonWebKeyECTypes.P256 || crv == JsonWebKeyECTypes.P384 || crv == JsonWebKeyECTypes.P521; } internal static string GetRsaSigningAlgorithmValue(IdentityServerConstants.RsaSigningAlgorithm value) { return value switch { IdentityServerConstants.RsaSigningAlgorithm.RS256 => SecurityAlgorithms.RsaSha256, IdentityServerConstants.RsaSigningAlgorithm.RS384 => SecurityAlgorithms.RsaSha384, IdentityServerConstants.RsaSigningAlgorithm.RS512 => SecurityAlgorithms.RsaSha512, IdentityServerConstants.RsaSigningAlgorithm.PS256 => SecurityAlgorithms.RsaSsaPssSha256, IdentityServerConstants.RsaSigningAlgorithm.PS384 => SecurityAlgorithms.RsaSsaPssSha384, IdentityServerConstants.RsaSigningAlgorithm.PS512 => SecurityAlgorithms.RsaSsaPssSha512, _ => throw new ArgumentException("Invalid RSA signing algorithm value", nameof(value)), }; } internal static string GetECDsaSigningAlgorithmValue(IdentityServerConstants.ECDsaSigningAlgorithm value) { return value switch { IdentityServerConstants.ECDsaSigningAlgorithm.ES256 => SecurityAlgorithms.EcdsaSha256, IdentityServerConstants.ECDsaSigningAlgorithm.ES384 => SecurityAlgorithms.EcdsaSha384, IdentityServerConstants.ECDsaSigningAlgorithm.ES512 => SecurityAlgorithms.EcdsaSha512, _ => throw new ArgumentException("Invalid ECDsa signing algorithm value", nameof(value)), }; } internal static X509Certificate2 FindCertificate(string name, StoreLocation location, NameType nameType) { X509Certificate2 certificate = null; if (location == StoreLocation.LocalMachine) { if (nameType == NameType.SubjectDistinguishedName) { certificate = X509.LocalMachine.My.SubjectDistinguishedName.Find(name, validOnly: false).FirstOrDefault(); } else if (nameType == NameType.Thumbprint) { certificate = X509.LocalMachine.My.Thumbprint.Find(name, validOnly: false).FirstOrDefault(); } } else { if (nameType == NameType.SubjectDistinguishedName) { certificate = X509.CurrentUser.My.SubjectDistinguishedName.Find(name, validOnly: false).FirstOrDefault(); } else if (nameType == NameType.Thumbprint) { certificate = X509.CurrentUser.My.Thumbprint.Find(name, validOnly: false).FirstOrDefault(); } } return certificate; } } /// /// Describes the string so we know what to search for in certificate store /// public enum NameType { /// /// subject distinguished name /// SubjectDistinguishedName, /// /// thumbprint /// Thumbprint } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/BuilderExtensions/Additional.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// Builder extension methods for registering additional services /// public static class IdentityServerBuilderExtensionsAdditional { /// /// Adds the extension grant validator. /// /// /// The builder. /// public static IIdentityServerBuilder AddExtensionGrantValidator(this IIdentityServerBuilder builder) where T : class, IExtensionGrantValidator { builder.Services.AddTransient(); return builder; } /// /// Adds a redirect URI validator. /// /// /// The builder. /// public static IIdentityServerBuilder AddRedirectUriValidator(this IIdentityServerBuilder builder) where T : class, IRedirectUriValidator { builder.Services.AddTransient(); return builder; } /// /// Adds a an "AppAuth" (OAuth 2.0 for Native Apps) compliant redirect URI validator (does strict validation but also allows http://127.0.0.1 with random port) /// /// The builder. /// public static IIdentityServerBuilder AddAppAuthRedirectUriValidator(this IIdentityServerBuilder builder) { return builder.AddRedirectUriValidator(); } /// /// Adds the resource owner validator. /// /// /// The builder. /// public static IIdentityServerBuilder AddResourceOwnerValidator(this IIdentityServerBuilder builder) where T : class, IResourceOwnerPasswordValidator { builder.Services.AddTransient(); return builder; } /// /// Adds the profile service. /// /// /// The builder. /// public static IIdentityServerBuilder AddProfileService(this IIdentityServerBuilder builder) where T : class, IProfileService { builder.Services.AddTransient(); return builder; } /// /// Adds a resource validator. /// /// /// The builder. /// public static IIdentityServerBuilder AddResourceValidator(this IIdentityServerBuilder builder) where T : class, IResourceValidator { builder.Services.AddTransient(); return builder; } /// /// Adds a scope parser. /// /// /// The builder. /// public static IIdentityServerBuilder AddScopeParser(this IIdentityServerBuilder builder) where T : class, IScopeParser { builder.Services.AddTransient(); return builder; } /// /// Adds a client store. /// /// /// The builder. /// public static IIdentityServerBuilder AddClientStore(this IIdentityServerBuilder builder) where T : class, IClientStore { builder.Services.TryAddTransient(typeof(T)); builder.Services.AddTransient>(); return builder; } /// /// Adds a resource store. /// /// /// The builder. /// public static IIdentityServerBuilder AddResourceStore(this IIdentityServerBuilder builder) where T : class, IResourceStore { builder.Services.AddTransient(); return builder; } /// /// Adds a device flow store. /// /// /// The builder. public static IIdentityServerBuilder AddDeviceFlowStore(this IIdentityServerBuilder builder) where T : class, IDeviceFlowStore { builder.Services.AddTransient(); return builder; } /// /// Adds a persisted grant store. /// /// The type of the concrete grant store that is registered in DI. /// The builder. /// The builder. public static IIdentityServerBuilder AddPersistedGrantStore(this IIdentityServerBuilder builder) where T : class, IPersistedGrantStore { builder.Services.AddTransient(); return builder; } /// /// Adds a CORS policy service. /// /// The type of the concrete scope store class that is registered in DI. /// The builder. /// public static IIdentityServerBuilder AddCorsPolicyService(this IIdentityServerBuilder builder) where T : class, ICorsPolicyService { builder.Services.AddTransient(); return builder; } /// /// Adds a CORS policy service cache. /// /// The type of the concrete CORS policy service that is registered in DI. /// The builder. /// public static IIdentityServerBuilder AddCorsPolicyCache(this IIdentityServerBuilder builder) where T : class, ICorsPolicyService { builder.Services.TryAddTransient(typeof(T)); builder.Services.AddTransient>(); return builder; } /// /// Adds the secret parser. /// /// /// The builder. /// public static IIdentityServerBuilder AddSecretParser(this IIdentityServerBuilder builder) where T : class, ISecretParser { builder.Services.AddTransient(); return builder; } /// /// Adds the secret validator. /// /// /// The builder. /// public static IIdentityServerBuilder AddSecretValidator(this IIdentityServerBuilder builder) where T : class, ISecretValidator { builder.Services.AddTransient(); return builder; } /// /// Adds the client store cache. /// /// The type of the concrete client store class that is registered in DI. /// The builder. /// public static IIdentityServerBuilder AddClientStoreCache(this IIdentityServerBuilder builder) where T : IClientStore { builder.Services.TryAddTransient(typeof(T)); builder.Services.AddTransient>(); builder.Services.AddTransient>>(); return builder; } /// /// Adds the client store cache. /// /// The type of the concrete scope store class that is registered in DI. /// The builder. /// public static IIdentityServerBuilder AddResourceStoreCache(this IIdentityServerBuilder builder) where T : IResourceStore { builder.Services.TryAddTransient(typeof(T)); builder.Services.AddTransient>(); return builder; } /// /// Adds the authorize interaction response generator. /// /// /// The builder. /// public static IIdentityServerBuilder AddAuthorizeInteractionResponseGenerator(this IIdentityServerBuilder builder) where T : class, IAuthorizeInteractionResponseGenerator { builder.Services.AddTransient(); return builder; } /// /// Adds the custom authorize request validator. /// /// /// The builder. /// public static IIdentityServerBuilder AddCustomAuthorizeRequestValidator(this IIdentityServerBuilder builder) where T : class, ICustomAuthorizeRequestValidator { builder.Services.AddTransient(); return builder; } /// /// Adds the custom authorize request validator. /// /// /// The builder. /// public static IIdentityServerBuilder AddCustomTokenRequestValidator(this IIdentityServerBuilder builder) where T : class, ICustomTokenRequestValidator { builder.Services.AddTransient(); return builder; } /// /// Adds support for client authentication using JWT bearer assertions. /// /// The builder. /// public static IIdentityServerBuilder AddJwtBearerClientAuthentication(this IIdentityServerBuilder builder) { builder.Services.TryAddTransient(); builder.AddSecretParser(); builder.AddSecretValidator(); return builder; } /// /// Adds a client configuration validator. /// /// /// The builder. /// public static IIdentityServerBuilder AddClientConfigurationValidator(this IIdentityServerBuilder builder) where T : class, IClientConfigurationValidator { builder.Services.AddTransient(); return builder; } /// /// Adds the X509 secret validators for mutual TLS. /// /// The builder. /// public static IIdentityServerBuilder AddMutualTlsSecretValidators(this IIdentityServerBuilder builder) { builder.AddSecretParser(); builder.AddSecretValidator(); builder.AddSecretValidator(); return builder; } /// /// Adds a custom back-channel logout service. /// /// /// The builder. /// public static IIdentityServerBuilder AddBackChannelLogoutService(this IIdentityServerBuilder builder) where T : class, IBackChannelLogoutService { builder.Services.AddTransient(); return builder; } // todo: check with later previews of ASP.NET Core if this is still required /// /// Adds configuration for the HttpClient used for back-channel logout notifications. /// /// The builder. /// The configruation callback. /// public static IHttpClientBuilder AddBackChannelLogoutHttpClient(this IIdentityServerBuilder builder, Action configureClient = null) { const string name = IdentityServerConstants.HttpClients.BackChannelLogoutHttpClient; IHttpClientBuilder httpBuilder; if (configureClient != null) { httpBuilder = builder.Services.AddHttpClient(name, configureClient); } else { httpBuilder = builder.Services.AddHttpClient(name) .ConfigureHttpClient(client => { client.Timeout = TimeSpan.FromSeconds(IdentityServerConstants.HttpClients.DefaultTimeoutSeconds); }); } builder.Services.AddTransient(s => { var httpClientFactory = s.GetRequiredService(); var httpClient = httpClientFactory.CreateClient(name); var loggerFactory = s.GetRequiredService(); return new DefaultBackChannelLogoutHttpClient(httpClient, loggerFactory); }); return httpBuilder; } // todo: check with later previews of ASP.NET Core if this is still required /// /// Adds configuration for the HttpClient used for JWT request_uri requests. /// /// The builder. /// The configruation callback. /// public static IHttpClientBuilder AddJwtRequestUriHttpClient(this IIdentityServerBuilder builder, Action configureClient = null) { const string name = IdentityServerConstants.HttpClients.JwtRequestUriHttpClient; IHttpClientBuilder httpBuilder; if (configureClient != null) { httpBuilder = builder.Services.AddHttpClient(name, configureClient); } else { httpBuilder = builder.Services.AddHttpClient(name) .ConfigureHttpClient(client => { client.Timeout = TimeSpan.FromSeconds(IdentityServerConstants.HttpClients.DefaultTimeoutSeconds); }); } builder.Services.AddTransient(s => { var httpClientFactory = s.GetRequiredService(); var httpClient = httpClientFactory.CreateClient(name); var loggerFactory = s.GetRequiredService(); var options = s.GetRequiredService(); return new DefaultJwtRequestUriHttpClient(httpClient, options, loggerFactory); }); return httpBuilder; } /// /// Adds a custom authorization request parameter store. /// /// /// The builder. /// public static IIdentityServerBuilder AddAuthorizationParametersMessageStore(this IIdentityServerBuilder builder) where T : class, IAuthorizationParametersMessageStore { builder.Services.AddTransient(); return builder; } /// /// Adds a custom user session. /// /// /// The builder. /// public static IIdentityServerBuilder AddUserSession(this IIdentityServerBuilder builder) where T : class, IUserSession { // This is added as scoped due to the note regarding the AuthenticateAsync // method in the IdentityServer8.Services.DefaultUserSession implementation. builder.Services.AddScoped(); return builder; } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/BuilderExtensions/Core.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// Builder extension methods for registering core services /// public static class IdentityServerBuilderExtensionsCore { /// /// Adds the required platform services. /// /// The builder. /// public static IIdentityServerBuilder AddRequiredPlatformServices(this IIdentityServerBuilder builder) { builder.Services.TryAddSingleton(); builder.Services.AddOptions(); builder.Services.AddSingleton( resolver => resolver.GetRequiredService>().Value); builder.Services.AddHttpClient(); return builder; } /// /// Adds the default cookie handlers and corresponding configuration /// /// The builder. /// public static IIdentityServerBuilder AddCookieAuthentication(this IIdentityServerBuilder builder) { builder.Services.AddAuthentication(IdentityServerConstants.DefaultCookieAuthenticationScheme) .AddCookie(IdentityServerConstants.DefaultCookieAuthenticationScheme) .AddCookie(IdentityServerConstants.ExternalCookieAuthenticationScheme); builder.Services.AddSingleton, ConfigureInternalCookieOptions>(); builder.Services.AddSingleton, PostConfigureInternalCookieOptions>(); builder.Services.AddTransientDecorator(); builder.Services.AddTransientDecorator(); return builder; } /// /// Adds the default endpoints. /// /// The builder. /// public static IIdentityServerBuilder AddDefaultEndpoints(this IIdentityServerBuilder builder) { builder.Services.AddTransient(); builder.AddEndpoint(EndpointNames.Authorize, ProtocolRoutePaths.AuthorizeCallback.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.Authorize, ProtocolRoutePaths.Authorize.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.CheckSession, ProtocolRoutePaths.CheckSession.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.DeviceAuthorization, ProtocolRoutePaths.DeviceAuthorization.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.Discovery, ProtocolRoutePaths.DiscoveryWebKeys.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.Discovery, ProtocolRoutePaths.DiscoveryConfiguration.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.EndSession, ProtocolRoutePaths.EndSessionCallback.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.EndSession, ProtocolRoutePaths.EndSession.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.Introspection, ProtocolRoutePaths.Introspection.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.Revocation, ProtocolRoutePaths.Revocation.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.Token, ProtocolRoutePaths.Token.EnsureLeadingSlash()); builder.AddEndpoint(EndpointNames.UserInfo, ProtocolRoutePaths.UserInfo.EnsureLeadingSlash()); return builder; } /// /// Adds the endpoint. /// /// /// The builder. /// The name. /// The path. /// public static IIdentityServerBuilder AddEndpoint(this IIdentityServerBuilder builder, string name, PathString path) where T : class, IEndpointHandler { builder.Services.AddTransient(); builder.Services.AddSingleton(new IdentityServer8.Hosting.Endpoint(name, path, typeof(T))); return builder; } /// /// Adds the core services. /// /// The builder. /// public static IIdentityServerBuilder AddCoreServices(this IIdentityServerBuilder builder) { builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.AddScoped(); builder.Services.AddTransient(typeof(MessageCookie<>)); builder.Services.AddCors(); builder.Services.AddTransientDecorator(); return builder; } /// /// Adds the pluggable services. /// /// The builder. /// public static IIdentityServerBuilder AddPluggableServices(this IIdentityServerBuilder builder) { builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient, ProtectedDataMessageStore>(); builder.Services.TryAddTransient, ProtectedDataMessageStore>(); builder.Services.TryAddTransient, ProtectedDataMessageStore>(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.AddJwtRequestUriHttpClient(); builder.AddBackChannelLogoutHttpClient(); builder.Services.AddTransient(); builder.Services.AddTransient(); builder.Services.TryAddTransient(); builder.Services.AddDistributedMemoryCache(); return builder; } /// /// Adds the validators. /// /// The builder. /// public static IIdentityServerBuilder AddValidators(this IIdentityServerBuilder builder) { // core builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); // optional builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); return builder; } /// /// Adds the response generators. /// /// The builder. /// public static IIdentityServerBuilder AddResponseGenerators(this IIdentityServerBuilder builder) { builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); builder.Services.TryAddTransient(); return builder; } /// /// Adds the default secret parsers. /// /// The builder. /// public static IIdentityServerBuilder AddDefaultSecretParsers(this IIdentityServerBuilder builder) { builder.Services.AddTransient(); builder.Services.AddTransient(); return builder; } /// /// Adds the default secret validators. /// /// The builder. /// public static IIdentityServerBuilder AddDefaultSecretValidators(this IIdentityServerBuilder builder) { builder.Services.AddTransient(); return builder; } internal static void AddTransientDecorator(this IServiceCollection services) where TService : class where TImplementation : class, TService { services.AddDecorator(); services.AddTransient(); } internal static void AddDecorator(this IServiceCollection services) { var registration = services.LastOrDefault(x => x.ServiceType == typeof(TService)); if (registration == null) { throw new InvalidOperationException("Service type: " + typeof(TService).Name + " not registered."); } if (services.Any(x => x.ServiceType == typeof(Decorator))) { throw new InvalidOperationException("Decorator already registered for type: " + typeof(TService).Name + "."); } services.Remove(registration); if (registration.ImplementationInstance != null) { var type = registration.ImplementationInstance.GetType(); var innerType = typeof(Decorator<,>).MakeGenericType(typeof(TService), type); services.Add(new ServiceDescriptor(typeof(Decorator), innerType, ServiceLifetime.Transient)); services.Add(new ServiceDescriptor(type, registration.ImplementationInstance)); } else if (registration.ImplementationFactory != null) { services.Add(new ServiceDescriptor(typeof(Decorator), provider => { return new DisposableDecorator((TService)registration.ImplementationFactory(provider)); }, registration.Lifetime)); } else { var type = registration.ImplementationType; var innerType = typeof(Decorator<,>).MakeGenericType(typeof(TService), registration.ImplementationType); services.Add(new ServiceDescriptor(typeof(Decorator), innerType, ServiceLifetime.Transient)); services.Add(new ServiceDescriptor(type, type, registration.Lifetime)); } } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/BuilderExtensions/Crypto.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; using JsonWebKey = Microsoft.IdentityModel.Tokens.JsonWebKey; namespace Microsoft.Extensions.DependencyInjection; /// /// Builder extension methods for registering crypto services /// public static class IdentityServerBuilderExtensionsCrypto { /// /// Sets the signing credential. /// /// The builder. /// The credential. /// public static IIdentityServerBuilder AddSigningCredential(this IIdentityServerBuilder builder, SigningCredentials credential) { if (!(credential.Key is AsymmetricSecurityKey || credential.Key is IdentityModel.Tokens.JsonWebKey && ((IdentityModel.Tokens.JsonWebKey)credential.Key).HasPrivateKey)) { throw new InvalidOperationException("Signing key is not asymmetric"); } if (!IdentityServerConstants.SupportedSigningAlgorithms.Contains(credential.Algorithm, StringComparer.Ordinal)) { throw new InvalidOperationException($"Signing algorithm {credential.Algorithm} is not supported."); } if (credential.Key is ECDsaSecurityKey key && !CryptoHelper.IsValidCurveForAlgorithm(key, credential.Algorithm)) { throw new InvalidOperationException("Invalid curve for signing algorithm"); } if (credential.Key is IdentityModel.Tokens.JsonWebKey jsonWebKey) { if (jsonWebKey.Kty == JsonWebAlgorithmsKeyTypes.EllipticCurve && !CryptoHelper.IsValidCrvValueForAlgorithm(jsonWebKey.Crv)) throw new InvalidOperationException("Invalid crv value for signing algorithm"); } builder.Services.AddSingleton(new InMemorySigningCredentialsStore(credential)); var keyInfo = new SecurityKeyInfo { Key = credential.Key, SigningAlgorithm = credential.Algorithm }; builder.Services.AddSingleton(new InMemoryValidationKeysStore(new[] { keyInfo })); return builder; } /// /// Sets the signing credential. /// /// The builder. /// The certificate. /// The signing algorithm (defaults to RS256) /// /// /// X509 certificate does not have a private key. public static IIdentityServerBuilder AddSigningCredential(this IIdentityServerBuilder builder, X509Certificate2 certificate, string signingAlgorithm = SecurityAlgorithms.RsaSha256) { if (certificate == null) throw new ArgumentNullException(nameof(certificate)); if (!certificate.HasPrivateKey) { throw new InvalidOperationException("X509 certificate does not have a private key."); } // add signing algorithm name to key ID to allow using the same key for two different algorithms (e.g. RS256 and PS56); var key = new X509SecurityKey(certificate); key.KeyId += signingAlgorithm; var credential = new SigningCredentials(key, signingAlgorithm); return builder.AddSigningCredential(credential); } /// /// Sets the signing credential. /// /// The builder. /// The name. /// The location. /// Name parameter can be either a distinguished name or a thumbprint /// The signing algorithm (defaults to RS256) /// certificate: '{name}' public static IIdentityServerBuilder AddSigningCredential( this IIdentityServerBuilder builder, string name, StoreLocation location = StoreLocation.LocalMachine, NameType nameType = NameType.SubjectDistinguishedName, string signingAlgorithm = SecurityAlgorithms.RsaSha256) { var certificate = CryptoHelper.FindCertificate(name, location, nameType); if (certificate == null) throw new InvalidOperationException($"certificate: '{name}' not found in certificate store"); return builder.AddSigningCredential(certificate, signingAlgorithm); } /// /// Sets the signing credential. /// /// The builder. /// The key. /// The signing algorithm /// public static IIdentityServerBuilder AddSigningCredential(this IIdentityServerBuilder builder, SecurityKey key, string signingAlgorithm) { var credential = new SigningCredentials(key, signingAlgorithm); return builder.AddSigningCredential(credential); } /// /// Sets an RSA-based signing credential. /// /// The builder. /// The RSA key. /// The signing algorithm /// public static IIdentityServerBuilder AddSigningCredential(this IIdentityServerBuilder builder, RsaSecurityKey key, IdentityServerConstants.RsaSigningAlgorithm signingAlgorithm) { var credential = new SigningCredentials(key, CryptoHelper.GetRsaSigningAlgorithmValue(signingAlgorithm)); return builder.AddSigningCredential(credential); } /// /// Sets an ECDsa-based signing credential. /// /// The builder. /// The ECDsa key. /// The signing algorithm /// public static IIdentityServerBuilder AddSigningCredential(this IIdentityServerBuilder builder, ECDsaSecurityKey key, IdentityServerConstants.ECDsaSigningAlgorithm signingAlgorithm) { var credential = new SigningCredentials(key, CryptoHelper.GetECDsaSigningAlgorithmValue(signingAlgorithm)); return builder.AddSigningCredential(credential); } /// /// Sets the temporary signing credential. /// /// The builder. /// Specifies if the temporary key should be persisted to disk. /// The filename. /// The signing algorithm (defaults to RS256) /// public static IIdentityServerBuilder AddDeveloperSigningCredential( this IIdentityServerBuilder builder, bool persistKey = true, string filename = null, IdentityServerConstants.RsaSigningAlgorithm signingAlgorithm = IdentityServerConstants.RsaSigningAlgorithm.RS256) { if (filename == null) { filename = Path.Combine(Directory.GetCurrentDirectory(), "tempkey.jwk"); } if (File.Exists(filename)) { var json = File.ReadAllText(filename); var jwk = new JsonWebKey(json); return builder.AddSigningCredential(jwk, jwk.Alg); } else { var key = CryptoHelper.CreateRsaSecurityKey(); var jwk = JsonWebKeyConverter.ConvertFromRSASecurityKey(key); jwk.Alg = signingAlgorithm.ToString(); if (persistKey) { File.WriteAllText(filename, JsonConvert.SerializeObject(jwk)); } return builder.AddSigningCredential(key, signingAlgorithm); } } /// /// Adds the validation keys. /// /// The builder. /// The keys. /// public static IIdentityServerBuilder AddValidationKey(this IIdentityServerBuilder builder, params SecurityKeyInfo[] keys) { builder.Services.AddSingleton(new InMemoryValidationKeysStore(keys)); return builder; } /// /// Adds an RSA-based validation key. /// /// The builder. /// The RSA key /// The RSA-based signing algorithm /// public static IIdentityServerBuilder AddValidationKey( this IIdentityServerBuilder builder, RsaSecurityKey key, IdentityServerConstants.RsaSigningAlgorithm signingAlgorithm = IdentityServerConstants.RsaSigningAlgorithm.RS256) { var keyInfo = new SecurityKeyInfo { Key = key, SigningAlgorithm = CryptoHelper.GetRsaSigningAlgorithmValue(signingAlgorithm) }; return builder.AddValidationKey(keyInfo); } /// /// Adds an ECDSA-based validation key. /// /// The builder. /// The ECDSA key /// The ECDSA-based signing algorithm /// public static IIdentityServerBuilder AddValidationKey( this IIdentityServerBuilder builder, ECDsaSecurityKey key, IdentityServerConstants.ECDsaSigningAlgorithm signingAlgorithm = IdentityServerConstants.ECDsaSigningAlgorithm.ES256) { var keyInfo = new SecurityKeyInfo { Key = key, SigningAlgorithm = CryptoHelper.GetECDsaSigningAlgorithmValue(signingAlgorithm) }; return builder.AddValidationKey(keyInfo); } /// /// Adds the validation key. /// /// The builder. /// The certificate. /// The signing algorithm /// /// public static IIdentityServerBuilder AddValidationKey( this IIdentityServerBuilder builder, X509Certificate2 certificate, string signingAlgorithm = SecurityAlgorithms.RsaSha256) { if (certificate == null) throw new ArgumentNullException(nameof(certificate)); // add signing algorithm name to key ID to allow using the same key for two different algorithms (e.g. RS256 and PS56); var key = new X509SecurityKey(certificate); key.KeyId += signingAlgorithm; var keyInfo = new SecurityKeyInfo { Key = key, SigningAlgorithm = signingAlgorithm }; return builder.AddValidationKey(keyInfo); } /// /// Adds the validation key from the certificate store. /// /// The builder. /// The name. /// The location. /// Name parameter can be either a distinguished name or a thumbprint /// The signing algorithm public static IIdentityServerBuilder AddValidationKey( this IIdentityServerBuilder builder, string name, StoreLocation location = StoreLocation.LocalMachine, NameType nameType = NameType.SubjectDistinguishedName, string signingAlgorithm = SecurityAlgorithms.RsaSha256) { var certificate = CryptoHelper.FindCertificate(name, location, nameType); if (certificate == null) throw new InvalidOperationException($"certificate: '{name}' not found in certificate store"); return builder.AddValidationKey(certificate, signingAlgorithm); } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/BuilderExtensions/InMemory.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// Builder extension methods for registering in-memory services /// public static class IdentityServerBuilderExtensionsInMemory { /// /// Adds the in memory caching. /// /// The builder. /// public static IIdentityServerBuilder AddInMemoryCaching(this IIdentityServerBuilder builder) { builder.Services.TryAddSingleton(); builder.Services.TryAddTransient(typeof(ICache<>), typeof(DefaultCache<>)); return builder; } /// /// Adds the in memory identity resources. /// /// The builder. /// The identity resources. /// public static IIdentityServerBuilder AddInMemoryIdentityResources(this IIdentityServerBuilder builder, IEnumerable identityResources) { builder.Services.AddSingleton(identityResources); builder.AddResourceStore(); return builder; } /// /// Adds the in memory identity resources. /// /// The builder. /// The configuration section containing the configuration data. /// public static IIdentityServerBuilder AddInMemoryIdentityResources(this IIdentityServerBuilder builder, IConfigurationSection section) { var resources = new List(); section.Bind(resources); return builder.AddInMemoryIdentityResources(resources); } /// /// Adds the in memory API resources. /// /// The builder. /// The API resources. /// public static IIdentityServerBuilder AddInMemoryApiResources(this IIdentityServerBuilder builder, IEnumerable apiResources) { builder.Services.AddSingleton(apiResources); builder.AddResourceStore(); return builder; } /// /// Adds the in memory API resources. /// /// The builder. /// The configuration section containing the configuration data. /// public static IIdentityServerBuilder AddInMemoryApiResources(this IIdentityServerBuilder builder, IConfigurationSection section) { var resources = new List(); section.Bind(resources); return builder.AddInMemoryApiResources(resources); } /// /// Adds the in memory API scopes. /// /// The builder. /// The API scopes. /// public static IIdentityServerBuilder AddInMemoryApiScopes(this IIdentityServerBuilder builder, IEnumerable apiScopes) { builder.Services.AddSingleton(apiScopes); builder.AddResourceStore(); return builder; } /// /// Adds the in memory scopes. /// /// The builder. /// The configuration section containing the configuration data. /// public static IIdentityServerBuilder AddInMemoryApiScopes(this IIdentityServerBuilder builder, IConfigurationSection section) { var resources = new List(); section.Bind(resources); return builder.AddInMemoryApiScopes(resources); } /// /// Adds the in memory clients. /// /// The builder. /// The clients. /// public static IIdentityServerBuilder AddInMemoryClients(this IIdentityServerBuilder builder, IEnumerable clients) { builder.Services.AddSingleton(clients); builder.AddClientStore(); var existingCors = builder.Services.Where(x => x.ServiceType == typeof(ICorsPolicyService)).LastOrDefault(); if (existingCors != null && existingCors.ImplementationType == typeof(DefaultCorsPolicyService) && existingCors.Lifetime == ServiceLifetime.Transient) { // if our default is registered, then overwrite with the InMemoryCorsPolicyService // otherwise don't overwrite with the InMemoryCorsPolicyService, which uses the custom one registered by the host builder.Services.AddTransient(); } return builder; } /// /// Adds the in memory clients. /// /// The builder. /// The configuration section containing the configuration data. /// public static IIdentityServerBuilder AddInMemoryClients(this IIdentityServerBuilder builder, IConfigurationSection section) { var clients = new List(); section.Bind(clients); return builder.AddInMemoryClients(clients); } /// /// Adds the in memory stores. /// /// The builder. /// public static IIdentityServerBuilder AddInMemoryPersistedGrants(this IIdentityServerBuilder builder) { builder.Services.TryAddSingleton(); builder.Services.TryAddSingleton(); return builder; } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/ConfigureInternalCookieOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; internal class ConfigureInternalCookieOptions : IConfigureNamedOptions { private readonly IdentityServerOptions _idsrv; public ConfigureInternalCookieOptions(IdentityServerOptions idsrv) { _idsrv = idsrv; } public void Configure(CookieAuthenticationOptions options) { } public void Configure(string name, CookieAuthenticationOptions options) { if (name == IdentityServerConstants.DefaultCookieAuthenticationScheme) { options.SlidingExpiration = _idsrv.Authentication.CookieSlidingExpiration; options.ExpireTimeSpan = _idsrv.Authentication.CookieLifetime; options.Cookie.Name = IdentityServerConstants.DefaultCookieAuthenticationScheme; options.Cookie.IsEssential = true; options.Cookie.SameSite = _idsrv.Authentication.CookieSameSiteMode; options.LoginPath = ExtractLocalUrl(_idsrv.UserInteraction.LoginUrl); options.LogoutPath = ExtractLocalUrl(_idsrv.UserInteraction.LogoutUrl); if (_idsrv.UserInteraction.LoginReturnUrlParameter != null) { options.ReturnUrlParameter = _idsrv.UserInteraction.LoginReturnUrlParameter; } } if (name == IdentityServerConstants.ExternalCookieAuthenticationScheme) { options.Cookie.Name = IdentityServerConstants.ExternalCookieAuthenticationScheme; options.Cookie.IsEssential = true; // https://github.com/alexhiggins732/IdentityServer8/issues/2595 // need to set None because iOS 12 safari considers the POST back to the client from the // IdP as not safe, so cookies issued from response (with lax) then should not be honored. // so we need to make those cookies issued without same-site, thus the browser will // hold onto them and send on the next redirect to the callback page. // see: https://brockallen.com/2019/01/11/same-site-cookies-asp-net-core-and-external-authentication-providers/ options.Cookie.SameSite = _idsrv.Authentication.CookieSameSiteMode; } } private static string ExtractLocalUrl(string url) { if (url.IsLocalUrl()) { if (url.StartsWith("~/")) { url = url.Substring(1); } return url; } return null; } } internal class PostConfigureInternalCookieOptions : IPostConfigureOptions { private readonly IdentityServerOptions _idsrv; private readonly IOptions _authOptions; private readonly ILogger _logger; public PostConfigureInternalCookieOptions( IdentityServerOptions idsrv, IOptions authOptions, ILoggerFactory loggerFactory) { _idsrv = idsrv; _authOptions = authOptions; _logger = loggerFactory.CreateLogger("IdentityServer8.Startup"); } public void PostConfigure(string name, CookieAuthenticationOptions options) { var scheme = _idsrv.Authentication.CookieAuthenticationScheme ?? _authOptions.Value.DefaultAuthenticateScheme ?? _authOptions.Value.DefaultScheme; if (name == scheme) { _idsrv.UserInteraction.LoginUrl = _idsrv.UserInteraction.LoginUrl ?? options.LoginPath; _idsrv.UserInteraction.LoginReturnUrlParameter = _idsrv.UserInteraction.LoginReturnUrlParameter ?? options.ReturnUrlParameter; _idsrv.UserInteraction.LogoutUrl = _idsrv.UserInteraction.LogoutUrl ?? options.LogoutPath; _logger.LogDebug("Login Url: {url}", _idsrv.UserInteraction.LoginUrl); _logger.LogDebug("Login Return Url Parameter: {param}", _idsrv.UserInteraction.LoginReturnUrlParameter); _logger.LogDebug("Logout Url: {url}", _idsrv.UserInteraction.LogoutUrl); _logger.LogDebug("ConsentUrl Url: {url}", _idsrv.UserInteraction.ConsentUrl); _logger.LogDebug("Consent Return Url Parameter: {param}", _idsrv.UserInteraction.ConsentReturnUrlParameter); _logger.LogDebug("Error Url: {url}", _idsrv.UserInteraction.ErrorUrl); _logger.LogDebug("Error Id Parameter: {param}", _idsrv.UserInteraction.ErrorIdParameter); } } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/ConfigureOpenIdConnectOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; internal class ConfigureOpenIdConnectOptions : IPostConfigureOptions { private string[] _schemes; private readonly IHttpContextAccessor _httpContextAccessor; public ConfigureOpenIdConnectOptions(string[] schemes, IHttpContextAccessor httpContextAccessor) { _schemes = schemes ?? throw new ArgumentNullException(nameof(schemes)); _httpContextAccessor = httpContextAccessor ?? throw new ArgumentNullException(nameof(httpContextAccessor)); } public void PostConfigure(string name, OpenIdConnectOptions options) { // no schemes means configure them all if (_schemes.Length == 0 || _schemes.Contains(name)) { options.StateDataFormat = new DistributedCacheStateDataFormatter(_httpContextAccessor, name); } } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Decorator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration.DependencyInjection; internal class Decorator { public TService Instance { get; set; } public Decorator(TService instance) { Instance = instance; } } internal class Decorator : Decorator where TImpl : class, TService { public Decorator(TImpl instance) : base(instance) { } } internal class DisposableDecorator : Decorator, IDisposable { public DisposableDecorator(TService instance) : base(instance) { } public void Dispose() { (Instance as IDisposable)?.Dispose(); } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/IIdentityServerBuilder.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// IdentityServer builder Interface /// public interface IIdentityServerBuilder { /// /// Gets the services. /// /// /// The services. /// IServiceCollection Services { get; } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/IdentityServerBuilder.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// IdentityServer helper class for DI configuration /// public class IdentityServerBuilder : IIdentityServerBuilder { /// /// Initializes a new instance of the class. /// /// The services. /// services public IdentityServerBuilder(IServiceCollection services) { Services = services ?? throw new ArgumentNullException(nameof(services)); } /// /// Gets the services. /// /// /// The services. /// public IServiceCollection Services { get; } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/IdentityServerServiceCollectionExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// DI extension methods for adding IdentityServer /// public static class IdentityServerServiceCollectionExtensions { /// /// Creates a builder. /// /// The services. /// public static IIdentityServerBuilder AddIdentityServerBuilder(this IServiceCollection services) { return new IdentityServerBuilder(services); } /// /// Adds IdentityServer. /// /// The services. /// public static IIdentityServerBuilder AddIdentityServer(this IServiceCollection services) { var builder = services.AddIdentityServerBuilder(); builder .AddRequiredPlatformServices() .AddCookieAuthentication() .AddCoreServices() .AddDefaultEndpoints() .AddPluggableServices() .AddValidators() .AddResponseGenerators() .AddDefaultSecretParsers() .AddDefaultSecretValidators(); // provide default in-memory implementation, not suitable for most production scenarios builder.AddInMemoryPersistedGrants(); return builder; } /// /// Adds IdentityServer. /// /// The services. /// The setup action. /// public static IIdentityServerBuilder AddIdentityServer(this IServiceCollection services, Action setupAction) { services.Configure(setupAction); return services.AddIdentityServer(); } /// /// Adds the IdentityServer. /// /// The services. /// The configuration. /// public static IIdentityServerBuilder AddIdentityServer(this IServiceCollection services, IConfiguration configuration) { services.Configure(configuration); return services.AddIdentityServer(); } /// /// Configures the OpenIdConnect handlers to persist the state parameter into the server-side IDistributedCache. /// /// The services. /// The schemes to configure. If none provided, then all OpenIdConnect schemes will use the cache. public static IServiceCollection AddOidcStateDataFormatterCache(this IServiceCollection services, params string[] schemes) { services.AddSingleton>( svcs => new ConfigureOpenIdConnectOptions( schemes, svcs.GetRequiredService()) ); return services; } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/AuthenticationOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Configures the login and logout views and behavior. /// public class AuthenticationOptions { /// /// Sets the cookie authentication scheme configured by the host used for interactive users. If not set, the scheme will inferred from the host's default authentication scheme. /// This setting is typically used when AddPolicyScheme is used in the host as the default scheme. /// public string CookieAuthenticationScheme { get; set; } /// /// Sets the cookie lifetime (only effective if the IdentityServer-provided cookie handler is used) /// public TimeSpan CookieLifetime { get; set; } = Constants.DefaultCookieTimeSpan; /// /// Specified if the cookie should be sliding or not (only effective if the built-in cookie middleware is used) /// public bool CookieSlidingExpiration { get; set; } = false; /// /// Specifies the SameSite mode for the internal authentication and temp cookie /// public SameSiteMode CookieSameSiteMode { get; set; } = SameSiteMode.None; /// /// Indicates if user must be authenticated to accept parameters to end session endpoint. Defaults to false. /// /// /// true if required; otherwise, false. /// public bool RequireAuthenticatedUserForSignOutMessage { get; set; } = false; /// /// Gets or sets the name of the cookie used for the check session endpoint. /// public string CheckSessionCookieName { get; set; } = IdentityServerConstants.DefaultCheckSessionCookieName; /// /// Gets or sets the domain of the cookie used for the check session endpoint. Defaults to null. /// public string CheckSessionCookieDomain { get; set; } /// /// Gets or sets the SameSite mode of the cookie used for the check session endpoint. Defaults to SameSiteMode.None. /// public SameSiteMode CheckSessionCookieSameSiteMode { get; set; } = SameSiteMode.None; /// /// If set, will require frame-src CSP headers being emitting on the end session callback endpoint which renders iframes to clients for front-channel signout notification. /// public bool RequireCspFrameSrcForSignout { get; set; } = true; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/CachingOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Caching options. /// public class CachingOptions { private static readonly TimeSpan Default = TimeSpan.FromMinutes(15); /// /// Gets or sets the client store expiration. /// /// /// The client store expiration. /// public TimeSpan ClientStoreExpiration { get; set; } = Default; /// /// Gets or sets the scope store expiration. /// /// /// The scope store expiration. /// public TimeSpan ResourceStoreExpiration { get; set; } = Default; /// /// Gets or sets the CORS origin expiration. /// public TimeSpan CorsExpiration { get; set; } = Default; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/CorsOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Options for CORS /// public class CorsOptions { /// /// Gets or sets the name of the cors policy. /// /// /// The name of the cors policy. /// public string CorsPolicyName { get; set; } = Constants.IdentityServerName; /// /// The value to be used in the preflight `Access-Control-Max-Age` response header. /// public TimeSpan? PreflightCacheDuration { get; set; } /// /// Gets or sets the cors paths. /// /// /// The cors paths. /// public ICollection CorsPaths { get; set; } = Constants.ProtocolRoutePaths.CorsPaths.Select(x => new PathString(x.EnsureLeadingSlash())).ToList(); } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/CspOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Options for Content Security Policy /// public class CspOptions { /// /// Gets or sets the minimum CSP level. /// public CspLevel Level { get; set; } = CspLevel.Two; /// /// Gets or sets a value indicating whether the deprected X-Content-Security-Policy header should be added. /// public bool AddDeprecatedHeader { get; set; } = true; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/DeviceFlowOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Configures device flow /// public class DeviceFlowOptions { /// /// Gets or sets the default type of the user code. /// /// /// The default type of the user code. /// public string DefaultUserCodeType { get; set; } = IdentityServerConstants.UserCodeTypes.Numeric; /// /// Gets or sets the polling interval in seconds. /// /// /// The interval in seconds. /// public int Interval { get; set; } = 5; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/DiscoveryOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Options class to configure discovery endpoint /// public class DiscoveryOptions { /// /// Show endpoints /// public bool ShowEndpoints { get; set; } = true; /// /// Show signing keys /// public bool ShowKeySet { get; set; } = true; /// /// Show identity scopes /// public bool ShowIdentityScopes { get; set; } = true; /// /// Show API scopes /// public bool ShowApiScopes { get; set; } = true; /// /// Show identity claims /// public bool ShowClaims { get; set; } = true; /// /// Show response types /// public bool ShowResponseTypes { get; set; } = true; /// /// Show response modes /// public bool ShowResponseModes { get; set; } = true; /// /// Show standard grant types /// public bool ShowGrantTypes { get; set; } = true; /// /// Show custom grant types /// public bool ShowExtensionGrantTypes { get; set; } = true; /// /// Show token endpoint authentication methods /// public bool ShowTokenEndpointAuthenticationMethods { get; set; } = true; /// /// Turns relative paths that start with ~/ into absolute paths /// public bool ExpandRelativePathsInCustomEntries { get; set; } = true; /// /// Sets the maxage value of the cache control header (in seconds) of the HTTP response. This gives clients a hint how often they should refresh their cached copy of the discovery document. If set to 0 no-cache headers will be set. Defaults to null, which does not set the header. /// /// /// The cache interval in seconds. /// public int? ResponseCacheInterval { get; set; } = null; /// /// Adds custom entries to the discovery document /// public Dictionary CustomEntries { get; set; } = new Dictionary(); } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/EndpointOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Configures which endpoints are enabled or disabled. /// public class EndpointsOptions { /// /// Gets or sets a value indicating whether the authorize endpoint is enabled. /// /// /// true if the authorize endpoint is enabled; otherwise, false. /// public bool EnableAuthorizeEndpoint { get; set; } = true; /// /// Gets or sets if JWT request_uri processing is enabled on the authorize endpoint. /// public bool EnableJwtRequestUri { get; set; } = false; /// /// Gets or sets a value indicating whether the token endpoint is enabled. /// /// /// true if the token endpoint is enabled; otherwise, false. /// public bool EnableTokenEndpoint { get; set; } = true; /// /// Gets or sets a value indicating whether the user info endpoint is enabled. /// /// /// true if the user info endpoint is enabled; otherwise, false. /// public bool EnableUserInfoEndpoint { get; set; } = true; /// /// Gets or sets a value indicating whether the discovery document endpoint is enabled. /// /// /// true if the disdovery document endpoint is enabled; otherwise, false. /// public bool EnableDiscoveryEndpoint { get; set; } = true; /// /// Gets or sets a value indicating whether the end session endpoint is enabled. /// /// /// true if the end session endpoint is enabled; otherwise, false. /// public bool EnableEndSessionEndpoint { get; set; } = true; /// /// Gets or sets a value indicating whether the check session endpoint is enabled. /// /// /// true if the check session endpoint is enabled; otherwise, false. /// public bool EnableCheckSessionEndpoint { get; set; } = true; /// /// Gets or sets a value indicating whether the token revocation endpoint is enabled. /// /// /// true if the token revocation endpoint is enabled; otherwise, false. /// public bool EnableTokenRevocationEndpoint { get; set; } = true; /// /// Gets or sets a value indicating whether the introspection endpoint is enabled. /// /// /// true if the introspection endpoint is enabled; otherwise, false. /// public bool EnableIntrospectionEndpoint { get; set; } = true; /// /// Gets or sets a value indicating whether the device authorization endpoint is enabled. /// /// /// true if the device authorization endpoint is enabled; otherwise, false. /// public bool EnableDeviceAuthorizationEndpoint { get; set; } = true; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/EventsOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Configures events /// public class EventsOptions { /// /// Gets or sets a value indicating whether to raise success events. /// /// /// true if success event should be raised; otherwise, false. /// public bool RaiseSuccessEvents { get; set; } = false; /// /// Gets or sets a value indicating whether to raise failure events. /// /// /// true if failure events should be raised; otherwise, false. /// public bool RaiseFailureEvents { get; set; } = false; /// /// Gets or sets a value indicating whether to raise information events. /// /// /// true if information events should be raised; otherwise, false. /// public bool RaiseInformationEvents { get; set; } = false; /// /// Gets or sets a value indicating whether to raise error events. /// /// /// true if error events should be raised; otherwise, false. /// public bool RaiseErrorEvents { get; set; } = false; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/IdentityServerOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// The IdentityServerOptions class is the top level container for all configuration settings of IdentityServer. /// public class IdentityServerOptions { /// /// Gets or sets the unique name of this server instance, e.g. https://myissuer.com. /// If not set, the issuer name is inferred from the request /// /// /// Unique name of this server instance, e.g. https://myissuer.com /// public string IssuerUri { get; set; } /// /// Set to false to preserve the original casing of the IssuerUri. Defaults to true. /// public bool LowerCaseIssuerUri { get; set; } = true; /// /// Gets or sets the value for the JWT typ header for access tokens. /// /// /// The JWT typ value. /// public string AccessTokenJwtType { get; set; } = "at+jwt"; /// /// Emits an aud claim with the format issuer/resources. That's needed for some older access token validation plumbing. Defaults to false. /// public bool EmitStaticAudienceClaim { get; set; } = false; /// /// Specifies whether scopes in JWTs are emitted as array or string /// public bool EmitScopesAsSpaceDelimitedStringInJwt { get; set; } = false; /// /// Specifies whether the JWT typ and content-type for JWT secured authorization requests is checked according to IETF spec. /// This might break older OIDC conformant request objects. /// public bool StrictJarValidation { get; set; } = false; /// /// Gets or sets the endpoint configuration. /// /// /// The endpoints configuration. /// public EndpointsOptions Endpoints { get; set; } = new EndpointsOptions(); /// /// Gets or sets the discovery endpoint configuration. /// /// /// The discovery endpoint configuration. /// public DiscoveryOptions Discovery { get; set; } = new DiscoveryOptions(); /// /// Gets or sets the authentication options. /// /// /// The authentication options. /// public AuthenticationOptions Authentication { get; set; } = new AuthenticationOptions(); /// /// Gets or sets the events options. /// /// /// The events options. /// public EventsOptions Events { get; set; } = new EventsOptions(); /// /// Gets or sets the max input length restrictions. /// /// /// The length restrictions. /// public InputLengthRestrictions InputLengthRestrictions { get; set; } = new InputLengthRestrictions(); /// /// Gets or sets the options for the user interaction. /// /// /// The user interaction options. /// public UserInteractionOptions UserInteraction { get; set; } = new UserInteractionOptions(); /// /// Gets or sets the caching options. /// /// /// The caching options. /// public CachingOptions Caching { get; set; } = new CachingOptions(); /// /// Gets or sets the cors options. /// /// /// The cors options. /// public CorsOptions Cors { get; set; } = new CorsOptions(); /// /// Gets or sets the Content Security Policy options. /// public CspOptions Csp { get; set; } = new CspOptions(); /// /// Gets or sets the validation options. /// public ValidationOptions Validation { get; set; } = new ValidationOptions(); /// /// Gets or sets the device flow options. /// public DeviceFlowOptions DeviceFlow { get; set; } = new DeviceFlowOptions(); /// /// Gets or sets the logging options /// public LoggingOptions Logging { get; set; } = new LoggingOptions(); /// /// Gets or sets the mutual TLS options. /// public MutualTlsOptions MutualTls { get; set; } = new MutualTlsOptions(); } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/InputLengthRestrictions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// /// public class InputLengthRestrictions { private const int Default = 100; /// /// Max length for client_id /// public int ClientId { get; set; } = Default; /// /// Max length for external client secrets /// public int ClientSecret { get; set; } = Default; /// /// Max length for scope /// public int Scope { get; set; } = 300; /// /// Max length for redirect_uri /// public int RedirectUri { get; set; } = 400; /// /// Max length for nonce /// public int Nonce { get; set; } = 300; /// /// Max length for ui_locale /// public int UiLocale { get; set; } = Default; /// /// Max length for login_hint /// public int LoginHint { get; set; } = Default; /// /// Max length for acr_values /// public int AcrValues { get; set; } = 300; /// /// Max length for grant_type /// public int GrantType { get; set; } = Default; /// /// Max length for username /// public int UserName { get; set; } = Default; /// /// Max length for password /// public int Password { get; set; } = Default; /// /// Max length for CSP reports /// public int CspReport { get; set; } = 2000; /// /// Max length for external identity provider name /// public int IdentityProvider { get; set; } = Default; /// /// Max length for external identity provider errors /// public int ExternalError { get; set; } = Default; /// /// Max length for authorization codes /// public int AuthorizationCode { get; set; } = Default; /// /// Max length for device codes /// public int DeviceCode { get; set; } = Default; /// /// Max length for refresh tokens /// public int RefreshToken { get; set; } = Default; /// /// Max length for token handles /// public int TokenHandle { get; set; } = Default; /// /// Max length for JWTs /// public int Jwt { get; set; } = 51200; /// /// Min length for the code challenge /// public int CodeChallengeMinLength { get; } = 43; /// /// Max length for the code challenge /// public int CodeChallengeMaxLength { get; } = 128; /// /// Min length for the code verifier /// public int CodeVerifierMinLength { get; } = 43; /// /// Max length for the code verifier /// public int CodeVerifierMaxLength { get; } = 128; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/LoggingOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Options for configuring logging behavior /// public class LoggingOptions { /// /// /// public ICollection TokenRequestSensitiveValuesFilter { get; set; } = new HashSet { OidcConstants.TokenRequest.ClientSecret, OidcConstants.TokenRequest.Password, OidcConstants.TokenRequest.ClientAssertion, OidcConstants.TokenRequest.RefreshToken, OidcConstants.TokenRequest.DeviceCode }; /// /// /// public ICollection AuthorizeRequestSensitiveValuesFilter { get; set; } = new HashSet { OidcConstants.AuthorizeRequest.IdTokenHint }; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/MtlsOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Options for Mutual TLS features /// public class MutualTlsOptions { /// /// Specifies if MTLS support should be enabled /// public bool Enabled { get; set; } /// /// Specifies the name of the authentication handler for X.509 client certificates /// public string ClientCertificateAuthenticationScheme { get; set; } = "Certificate"; /// /// Specifies a separate domain to run the MTLS endpoints on. /// If the string does not contain any dots, a subdomain is assumed - e.g. main domain: identityserver.local, MTLS domain: mtls.identityserver.local /// If the string contains dots, a completely separate domain is assumend, e.g. main domain: identity.app.com, MTLS domain: mtls.app.com. In this case you must set a static issuer name on the options. /// public string DomainName { get; set; } /// /// Specifies whether a cnf claim gets emitted for access tokens if a client certificate was present. /// Normally the cnf claims only gets emitted if the client used the client certificate for authentication, /// setting this to true, will set the claim regardless of the authentication method. (defaults to false). /// public bool AlwaysEmitConfirmationClaim { get; set; } } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/UserInteractionOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// Options for aspects of the user interface. /// public class UserInteractionOptions { /// /// Gets or sets the login URL. If a local URL, the value must start with a leading slash. /// /// /// The login URL. /// public string LoginUrl { get; set; } //= Constants.UIConstants.DefaultRoutePaths.Login.EnsureLeadingSlash(); /// /// Gets or sets the login return URL parameter. /// /// /// The login return URL parameter. /// public string LoginReturnUrlParameter { get; set; } //= Constants.UIConstants.DefaultRoutePathParams.Login; /// /// Gets or sets the logout URL. If a local URL, the value must start with a leading slash. /// /// /// The logout URL. /// public string LogoutUrl { get; set; } //= Constants.UIConstants.DefaultRoutePaths.Logout.EnsureLeadingSlash(); /// /// Gets or sets the logout identifier parameter. /// /// /// The logout identifier parameter. /// public string LogoutIdParameter { get; set; } = Constants.UIConstants.DefaultRoutePathParams.Logout; /// /// Gets or sets the consent URL. If a local URL, the value must start with a leading slash. /// /// /// The consent URL. /// public string ConsentUrl { get; set; } = Constants.UIConstants.DefaultRoutePaths.Consent.EnsureLeadingSlash(); /// /// Gets or sets the consent return URL parameter. /// /// /// The consent return URL parameter. /// public string ConsentReturnUrlParameter { get; set; } = Constants.UIConstants.DefaultRoutePathParams.Consent; /// /// Gets or sets the error URL. If a local URL, the value must start with a leading slash. /// /// /// The error URL. /// public string ErrorUrl { get; set; } = Constants.UIConstants.DefaultRoutePaths.Error.EnsureLeadingSlash(); /// /// Gets or sets the error identifier parameter. /// /// /// The error identifier parameter. /// public string ErrorIdParameter { get; set; } = Constants.UIConstants.DefaultRoutePathParams.Error; /// /// Gets or sets the custom redirect return URL parameter. /// /// /// The custom redirect return URL parameter. /// public string CustomRedirectReturnUrlParameter { get; set; } = Constants.UIConstants.DefaultRoutePathParams.Custom; /// /// Gets or sets the cookie message threshold. This limits how many cookies are created, and older ones will be purged. /// /// /// The cookie message threshold. /// public int CookieMessageThreshold { get; set; } = Constants.UIConstants.CookieMessageThreshold; /// /// Gets or sets the device verification URL. If a local URL, the value must start with a leading slash. /// /// /// The device verification URL. /// public string DeviceVerificationUrl { get; set; } = Constants.UIConstants.DefaultRoutePaths.DeviceVerification; /// /// Gets or sets the device verification user code paramater. /// /// /// The device verification user code parameter. /// public string DeviceVerificationUserCodeParameter { get; set; } = Constants.UIConstants.DefaultRoutePathParams.UserCode; } ================================================ FILE: src/IdentityServer8/src/Configuration/DependencyInjection/Options/ValidationOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Configuration; /// /// The ValidationOptions contains settings that affect some of the default validation behavior. /// public class ValidationOptions { /// /// Collection of URI scheme prefixes that should never be used as custom URI schemes in the redirect_uri passed to tha authorize endpoint. /// public ICollection InvalidRedirectUriPrefixes { get; } = new HashSet { "javascript:", "file:", "data:", "mailto:", "ftp:", "blob:", "about:", "ssh:", "tel:", "view-source:", "ws:", "wss:" }; } ================================================ FILE: src/IdentityServer8/src/Configuration/IdentityServerApplicationBuilderExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.AspNetCore.Builder; /// /// Pipeline extension methods for adding IdentityServer /// public static class IdentityServerApplicationBuilderExtensions { /// /// Adds IdentityServer to the pipeline. /// /// The application. /// The options. /// public static IApplicationBuilder UseIdentityServer(this IApplicationBuilder app, IdentityServerMiddlewareOptions options = null) { app.Validate(); app.UseMiddleware(); app.ConfigureCors(); // it seems ok if we have UseAuthentication more than once in the pipeline -- // this will just re-run the various callback handlers and the default authN // handler, which just re-assigns the user on the context. claims transformation // will run twice, since that's not cached (whereas the authN handler result is) // related: https://github.com/aspnet/Security/issues/1399 if (options == null) options = new IdentityServerMiddlewareOptions(); options.AuthenticationMiddleware(app); app.UseMiddleware(); app.UseMiddleware(); return app; } internal static void Validate(this IApplicationBuilder app) { var loggerFactory = app.ApplicationServices.GetService(typeof(ILoggerFactory)) as ILoggerFactory; if (loggerFactory == null) throw new ArgumentNullException(nameof(loggerFactory)); var logger = loggerFactory.CreateLogger("IdentityServer8.Startup"); logger.LogInformation("Starting IdentityServer8 version {version}", typeof(IdentityServer8.Hosting.IdentityServerMiddleware).Assembly.GetCustomAttribute().InformationalVersion); var scopeFactory = app.ApplicationServices.GetService(); using (var scope = scopeFactory.CreateScope()) { var serviceProvider = scope.ServiceProvider; TestService(serviceProvider, typeof(IPersistedGrantStore), logger, "No storage mechanism for grants specified. Use the 'AddInMemoryPersistedGrants' extension method to register a development version."); TestService(serviceProvider, typeof(IClientStore), logger, "No storage mechanism for clients specified. Use the 'AddInMemoryClients' extension method to register a development version."); TestService(serviceProvider, typeof(IResourceStore), logger, "No storage mechanism for resources specified. Use the 'AddInMemoryIdentityResources' or 'AddInMemoryApiResources' extension method to register a development version."); var persistedGrants = serviceProvider.GetService(typeof(IPersistedGrantStore)); if (persistedGrants.GetType().FullName == typeof(InMemoryPersistedGrantStore).FullName) { logger.LogInformation("You are using the in-memory version of the persisted grant store. This will store consent decisions, authorization codes, refresh and reference tokens in memory only. If you are using any of those features in production, you want to switch to a different store implementation."); } var options = serviceProvider.GetRequiredService(); ValidateOptions(options, logger); ValidateAsync(serviceProvider, logger).GetAwaiter().GetResult(); } } private static async Task ValidateAsync(IServiceProvider services, ILogger logger) { var options = services.GetRequiredService(); var schemes = services.GetRequiredService(); if (await schemes.GetDefaultAuthenticateSchemeAsync() == null && options.Authentication.CookieAuthenticationScheme == null) { logger.LogWarning("No authentication scheme has been set. Setting either a default authentication scheme or a CookieAuthenticationScheme on IdentityServerOptions is required."); } else { AuthenticationScheme authenticationScheme = null; if (options.Authentication.CookieAuthenticationScheme != null) { authenticationScheme = await schemes.GetSchemeAsync(options.Authentication.CookieAuthenticationScheme); logger.LogInformation("Using explicitly configured authentication scheme {scheme} for IdentityServer", options.Authentication.CookieAuthenticationScheme); } else { authenticationScheme = await schemes.GetDefaultAuthenticateSchemeAsync(); logger.LogInformation("Using the default authentication scheme {scheme} for IdentityServer", authenticationScheme.Name); } if (!typeof(IAuthenticationSignInHandler).IsAssignableFrom(authenticationScheme.HandlerType)) { logger.LogInformation("Authentication scheme {scheme} is configured for IdentityServer, but it is not a scheme that supports signin (like cookies). If you support interactive logins via the browser, then a cookie-based scheme should be used.", authenticationScheme.Name); } logger.LogDebug("Using {scheme} as default ASP.NET Core scheme for authentication", (await schemes.GetDefaultAuthenticateSchemeAsync())?.Name); logger.LogDebug("Using {scheme} as default ASP.NET Core scheme for sign-in", (await schemes.GetDefaultSignInSchemeAsync())?.Name); logger.LogDebug("Using {scheme} as default ASP.NET Core scheme for sign-out", (await schemes.GetDefaultSignOutSchemeAsync())?.Name); logger.LogDebug("Using {scheme} as default ASP.NET Core scheme for challenge", (await schemes.GetDefaultChallengeSchemeAsync())?.Name); logger.LogDebug("Using {scheme} as default ASP.NET Core scheme for forbid", (await schemes.GetDefaultForbidSchemeAsync())?.Name); } } private static void ValidateOptions(IdentityServerOptions options, ILogger logger) { if (options.IssuerUri.IsPresent()) logger.LogDebug("Custom IssuerUri set to {0}", options.IssuerUri); // todo: perhaps different logging messages? //if (options.UserInteraction.LoginUrl.IsMissing()) throw new InvalidOperationException("LoginUrl is not configured"); //if (options.UserInteraction.LoginReturnUrlParameter.IsMissing()) throw new InvalidOperationException("LoginReturnUrlParameter is not configured"); //if (options.UserInteraction.LogoutUrl.IsMissing()) throw new InvalidOperationException("LogoutUrl is not configured"); if (options.UserInteraction.LogoutIdParameter.IsMissing()) throw new InvalidOperationException("LogoutIdParameter is not configured"); if (options.UserInteraction.ErrorUrl.IsMissing()) throw new InvalidOperationException("ErrorUrl is not configured"); if (options.UserInteraction.ErrorIdParameter.IsMissing()) throw new InvalidOperationException("ErrorIdParameter is not configured"); if (options.UserInteraction.ConsentUrl.IsMissing()) throw new InvalidOperationException("ConsentUrl is not configured"); if (options.UserInteraction.ConsentReturnUrlParameter.IsMissing()) throw new InvalidOperationException("ConsentReturnUrlParameter is not configured"); if (options.UserInteraction.CustomRedirectReturnUrlParameter.IsMissing()) throw new InvalidOperationException("CustomRedirectReturnUrlParameter is not configured"); if (options.Authentication.CheckSessionCookieName.IsMissing()) throw new InvalidOperationException("CheckSessionCookieName is not configured"); if (options.Cors.CorsPolicyName.IsMissing()) throw new InvalidOperationException("CorsPolicyName is not configured"); } internal static object TestService(IServiceProvider serviceProvider, Type service, ILogger logger, string message = null, bool doThrow = true) { var appService = serviceProvider.GetService(service); if (appService == null) { var error = message ?? $"Required service {service.FullName} is not registered in the DI container. Aborting startup"; logger.LogCritical(error); if (doThrow) { throw new InvalidOperationException(error); } } return appService; } } ================================================ FILE: src/IdentityServer8/src/Configuration/IdentityServerMiddlewareOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.AspNetCore.Builder; /// /// Options for the IdentityServer middleware /// public class IdentityServerMiddlewareOptions { /// /// Callback to wire up an authentication middleware /// public Action AuthenticationMiddleware { get; set; } = (app) => app.UseAuthentication(); } ================================================ FILE: src/IdentityServer8/src/Constants.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8; internal static class Constants { public const string IdentityServerName = "IdentityServer8"; public const string IdentityServerAuthenticationType = IdentityServerName; public const string ExternalAuthenticationMethod = "external"; public const string DefaultHashAlgorithm = "SHA256"; public static readonly TimeSpan DefaultCookieTimeSpan = TimeSpan.FromHours(10); public static readonly TimeSpan DefaultCacheDuration = TimeSpan.FromMinutes(60); public static readonly List SupportedResponseTypes = new List { OidcConstants.ResponseTypes.Code, OidcConstants.ResponseTypes.Token, OidcConstants.ResponseTypes.IdToken, OidcConstants.ResponseTypes.IdTokenToken, OidcConstants.ResponseTypes.CodeIdToken, OidcConstants.ResponseTypes.CodeToken, OidcConstants.ResponseTypes.CodeIdTokenToken }; public static readonly Dictionary ResponseTypeToGrantTypeMapping = new Dictionary { { OidcConstants.ResponseTypes.Code, GrantType.AuthorizationCode }, { OidcConstants.ResponseTypes.Token, GrantType.Implicit }, { OidcConstants.ResponseTypes.IdToken, GrantType.Implicit }, { OidcConstants.ResponseTypes.IdTokenToken, GrantType.Implicit }, { OidcConstants.ResponseTypes.CodeIdToken, GrantType.Hybrid }, { OidcConstants.ResponseTypes.CodeToken, GrantType.Hybrid }, { OidcConstants.ResponseTypes.CodeIdTokenToken, GrantType.Hybrid } }; public static readonly List AllowedGrantTypesForAuthorizeEndpoint = new List { GrantType.AuthorizationCode, GrantType.Implicit, GrantType.Hybrid }; public static readonly List SupportedCodeChallengeMethods = new List { OidcConstants.CodeChallengeMethods.Plain, OidcConstants.CodeChallengeMethods.Sha256 }; public enum ScopeRequirement { None, ResourceOnly, IdentityOnly, Identity } public static readonly Dictionary ResponseTypeToScopeRequirement = new Dictionary { { OidcConstants.ResponseTypes.Code, ScopeRequirement.None }, { OidcConstants.ResponseTypes.Token, ScopeRequirement.ResourceOnly }, { OidcConstants.ResponseTypes.IdToken, ScopeRequirement.IdentityOnly }, { OidcConstants.ResponseTypes.IdTokenToken, ScopeRequirement.Identity }, { OidcConstants.ResponseTypes.CodeIdToken, ScopeRequirement.Identity }, { OidcConstants.ResponseTypes.CodeToken, ScopeRequirement.Identity }, { OidcConstants.ResponseTypes.CodeIdTokenToken, ScopeRequirement.Identity } }; public static readonly Dictionary> AllowedResponseModesForGrantType = new Dictionary> { { GrantType.AuthorizationCode, new[] { OidcConstants.ResponseModes.Query, OidcConstants.ResponseModes.FormPost, OidcConstants.ResponseModes.Fragment } }, { GrantType.Hybrid, new[] { OidcConstants.ResponseModes.Fragment, OidcConstants.ResponseModes.FormPost }}, { GrantType.Implicit, new[] { OidcConstants.ResponseModes.Fragment, OidcConstants.ResponseModes.FormPost }} }; public static readonly List SupportedResponseModes = new List { OidcConstants.ResponseModes.FormPost, OidcConstants.ResponseModes.Query, OidcConstants.ResponseModes.Fragment }; public static string[] SupportedSubjectTypes = { "pairwise", "public" }; public static class SigningAlgorithms { public const string RSA_SHA_256 = "RS256"; } public static readonly List SupportedDisplayModes = new List { OidcConstants.DisplayModes.Page, OidcConstants.DisplayModes.Popup, OidcConstants.DisplayModes.Touch, OidcConstants.DisplayModes.Wap }; public static readonly List SupportedPromptModes = new List { OidcConstants.PromptModes.None, OidcConstants.PromptModes.Login, OidcConstants.PromptModes.Consent, OidcConstants.PromptModes.SelectAccount }; public static class KnownAcrValues { public const string HomeRealm = "idp:"; public const string Tenant = "tenant:"; public static readonly string[] All = { HomeRealm, Tenant }; } public static Dictionary ProtectedResourceErrorStatusCodes = new Dictionary { { OidcConstants.ProtectedResourceErrors.InvalidToken, 401 }, { OidcConstants.ProtectedResourceErrors.ExpiredToken, 401 }, { OidcConstants.ProtectedResourceErrors.InvalidRequest, 400 }, { OidcConstants.ProtectedResourceErrors.InsufficientScope, 403 } }; public static readonly Dictionary> ScopeToClaimsMapping = new Dictionary> { { IdentityServerConstants.StandardScopes.Profile, new[] { JwtClaimTypes.Name, JwtClaimTypes.FamilyName, JwtClaimTypes.GivenName, JwtClaimTypes.MiddleName, JwtClaimTypes.NickName, JwtClaimTypes.PreferredUserName, JwtClaimTypes.Profile, JwtClaimTypes.Picture, JwtClaimTypes.WebSite, JwtClaimTypes.Gender, JwtClaimTypes.BirthDate, JwtClaimTypes.ZoneInfo, JwtClaimTypes.Locale, JwtClaimTypes.UpdatedAt }}, { IdentityServerConstants.StandardScopes.Email, new[] { JwtClaimTypes.Email, JwtClaimTypes.EmailVerified }}, { IdentityServerConstants.StandardScopes.Address, new[] { JwtClaimTypes.Address }}, { IdentityServerConstants.StandardScopes.Phone, new[] { JwtClaimTypes.PhoneNumber, JwtClaimTypes.PhoneNumberVerified }}, { IdentityServerConstants.StandardScopes.OpenId, new[] { JwtClaimTypes.Subject }} }; public static class UIConstants { // the limit after which old messages are purged public const int CookieMessageThreshold = 2; public static class DefaultRoutePathParams { public const string Error = "errorId"; public const string Login = "returnUrl"; public const string Consent = "returnUrl"; public const string Logout = "logoutId"; public const string EndSessionCallback = "endSessionId"; public const string Custom = "returnUrl"; public const string UserCode = "userCode"; } public static class DefaultRoutePaths { public const string Login = "/account/login"; public const string Logout = "/account/logout"; public const string Consent = "/consent"; public const string Error = "/home/error"; public const string DeviceVerification = "/device"; } } public static class EndpointNames { public const string Authorize = "Authorize"; public const string Token = "Token"; public const string DeviceAuthorization = "DeviceAuthorization"; public const string Discovery = "Discovery"; public const string Introspection = "Introspection"; public const string Revocation = "Revocation"; public const string EndSession = "Endsession"; public const string CheckSession = "Checksession"; public const string UserInfo = "Userinfo"; } public static class ProtocolRoutePaths { public const string ConnectPathPrefix = "connect"; public const string Authorize = ConnectPathPrefix + "/authorize"; public const string AuthorizeCallback = Authorize + "/callback"; public const string DiscoveryConfiguration = ".well-known/openid-configuration"; public const string DiscoveryWebKeys = DiscoveryConfiguration + "/jwks"; public const string Token = ConnectPathPrefix + "/token"; public const string Revocation = ConnectPathPrefix + "/revocation"; public const string UserInfo = ConnectPathPrefix + "/userinfo"; public const string Introspection = ConnectPathPrefix + "/introspect"; public const string EndSession = ConnectPathPrefix + "/endsession"; public const string EndSessionCallback = EndSession + "/callback"; public const string CheckSession = ConnectPathPrefix + "/checksession"; public const string DeviceAuthorization = ConnectPathPrefix + "/deviceauthorization"; public const string MtlsPathPrefix = ConnectPathPrefix + "/mtls"; public const string MtlsToken = MtlsPathPrefix + "/token"; public const string MtlsRevocation = MtlsPathPrefix + "/revocation"; public const string MtlsIntrospection = MtlsPathPrefix + "/introspect"; public const string MtlsDeviceAuthorization = MtlsPathPrefix + "/deviceauthorization"; public static readonly string[] CorsPaths = { DiscoveryConfiguration, DiscoveryWebKeys, Token, UserInfo, Revocation }; } public static class EnvironmentKeys { public const string IdentityServerBasePath = "idsvr:IdentityServerBasePath"; [Obsolete("The IdentityServerOrigin constant is obsolete.")] public const string IdentityServerOrigin = "idsvr:IdentityServerOrigin"; // todo: deprecate public const string SignOutCalled = "idsvr:IdentityServerSignOutCalled"; } public static class TokenTypeHints { public const string RefreshToken = "refresh_token"; public const string AccessToken = "access_token"; } public static List SupportedTokenTypeHints = new List { TokenTypeHints.RefreshToken, TokenTypeHints.AccessToken }; public static class RevocationErrors { public const string UnsupportedTokenType = "unsupported_token_type"; } public class Filters { // filter for claims from an incoming access token (e.g. used at the user profile endpoint) public static readonly string[] ProtocolClaimsFilter = { JwtClaimTypes.AccessTokenHash, JwtClaimTypes.Audience, JwtClaimTypes.AuthorizedParty, JwtClaimTypes.AuthorizationCodeHash, JwtClaimTypes.ClientId, JwtClaimTypes.Expiration, JwtClaimTypes.IssuedAt, JwtClaimTypes.Issuer, JwtClaimTypes.JwtId, JwtClaimTypes.Nonce, JwtClaimTypes.NotBefore, JwtClaimTypes.ReferenceTokenId, JwtClaimTypes.SessionId, JwtClaimTypes.Scope }; // filter list for claims returned from profile service prior to creating tokens public static readonly string[] ClaimsServiceFilterClaimTypes = { // TODO: consider JwtClaimTypes.AuthenticationContextClassReference, JwtClaimTypes.AccessTokenHash, JwtClaimTypes.Audience, JwtClaimTypes.AuthenticationMethod, JwtClaimTypes.AuthenticationTime, JwtClaimTypes.AuthorizedParty, JwtClaimTypes.AuthorizationCodeHash, JwtClaimTypes.ClientId, JwtClaimTypes.Expiration, JwtClaimTypes.IdentityProvider, JwtClaimTypes.IssuedAt, JwtClaimTypes.Issuer, JwtClaimTypes.JwtId, JwtClaimTypes.Nonce, JwtClaimTypes.NotBefore, JwtClaimTypes.ReferenceTokenId, JwtClaimTypes.SessionId, JwtClaimTypes.Subject, JwtClaimTypes.Scope, JwtClaimTypes.Confirmation }; public static readonly string[] JwtRequestClaimTypesFilter = { JwtClaimTypes.Audience, JwtClaimTypes.Expiration, JwtClaimTypes.IssuedAt, JwtClaimTypes.Issuer, JwtClaimTypes.NotBefore, JwtClaimTypes.JwtId }; } public static class WsFedSignOut { public const string LogoutUriParameterName = "wa"; public const string LogoutUriParameterValue = "wsignoutcleanup1.0"; } public static class AuthorizationParamsStore { public const string MessageStoreIdParameterName = "authzId"; } public static class CurveOids { public const string P256 = "1.2.840.10045.3.1.7"; public const string P384 = "1.3.132.0.34"; public const string P521 = "1.3.132.0.35"; } } ================================================ FILE: src/IdentityServer8/src/Endpoints/AuthorizeCallbackEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; internal class AuthorizeCallbackEndpoint : AuthorizeEndpointBase { private readonly IConsentMessageStore _consentResponseStore; private readonly IAuthorizationParametersMessageStore _authorizationParametersMessageStore; public AuthorizeCallbackEndpoint( IEventService events, ILogger logger, IdentityServerOptions options, IAuthorizeRequestValidator validator, IAuthorizeInteractionResponseGenerator interactionGenerator, IAuthorizeResponseGenerator authorizeResponseGenerator, IUserSession userSession, IConsentMessageStore consentResponseStore, IAuthorizationParametersMessageStore authorizationParametersMessageStore = null) : base(events, logger, options, validator, interactionGenerator, authorizeResponseGenerator, userSession) { _consentResponseStore = consentResponseStore; _authorizationParametersMessageStore = authorizationParametersMessageStore; } public override async Task ProcessAsync(HttpContext context) { if (!HttpMethods.IsGet(context.Request.Method)) { Logger.LogWarning("Invalid HTTP method for authorize endpoint."); return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } Logger.LogDebug("Start authorize callback request"); var parameters = context.Request.Query.AsNameValueCollection(); if (_authorizationParametersMessageStore != null) { var messageStoreId = parameters[Constants.AuthorizationParamsStore.MessageStoreIdParameterName]; var entry = await _authorizationParametersMessageStore.ReadAsync(messageStoreId); parameters = entry?.Data.FromFullDictionary() ?? new NameValueCollection(); await _authorizationParametersMessageStore.DeleteAsync(messageStoreId); } var user = await UserSession.GetUserAsync(); var consentRequest = new ConsentRequest(parameters, user?.GetSubjectId()); var consent = await _consentResponseStore.ReadAsync(consentRequest.Id); if (consent != null && consent.Data == null) { return await CreateErrorResultAsync("consent message is missing data"); } try { var result = await ProcessAuthorizeRequestAsync(parameters, user, consent?.Data); Logger.LogTrace("End Authorize Request. Result type: {0}", result?.GetType().ToString() ?? "-none-"); return result; } finally { if (consent != null) { await _consentResponseStore.DeleteAsync(consentRequest.Id); } } } } ================================================ FILE: src/IdentityServer8/src/Endpoints/AuthorizeEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; internal class AuthorizeEndpoint : AuthorizeEndpointBase { public AuthorizeEndpoint( IEventService events, ILogger logger, IdentityServerOptions options, IAuthorizeRequestValidator validator, IAuthorizeInteractionResponseGenerator interactionGenerator, IAuthorizeResponseGenerator authorizeResponseGenerator, IUserSession userSession) : base(events, logger, options, validator, interactionGenerator, authorizeResponseGenerator, userSession) { } public override async Task ProcessAsync(HttpContext context) { Logger.LogDebug("Start authorize request"); NameValueCollection values; if (HttpMethods.IsGet(context.Request.Method)) { values = context.Request.Query.AsNameValueCollection(); } else if (HttpMethods.IsPost(context.Request.Method)) { if (!context.Request.HasApplicationFormContentType()) { return new StatusCodeResult(HttpStatusCode.UnsupportedMediaType); } values = context.Request.Form.AsNameValueCollection(); } else { return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } var user = await UserSession.GetUserAsync(); var result = await ProcessAuthorizeRequestAsync(values, user, null); Logger.LogTrace("End authorize request. result type: {0}", result?.GetType().ToString() ?? "-none-"); return result; } } ================================================ FILE: src/IdentityServer8/src/Endpoints/AuthorizeEndpointBase.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; internal abstract class AuthorizeEndpointBase : IEndpointHandler { private readonly IAuthorizeResponseGenerator _authorizeResponseGenerator; private readonly IEventService _events; private readonly IdentityServerOptions _options; private readonly IAuthorizeInteractionResponseGenerator _interactionGenerator; private readonly IAuthorizeRequestValidator _validator; protected AuthorizeEndpointBase( IEventService events, ILogger logger, IdentityServerOptions options, IAuthorizeRequestValidator validator, IAuthorizeInteractionResponseGenerator interactionGenerator, IAuthorizeResponseGenerator authorizeResponseGenerator, IUserSession userSession) { _events = events; _options = options; Logger = logger; _validator = validator; _interactionGenerator = interactionGenerator; _authorizeResponseGenerator = authorizeResponseGenerator; UserSession = userSession; } protected ILogger Logger { get; private set; } protected IUserSession UserSession { get; private set; } public abstract Task ProcessAsync(HttpContext context); internal async Task ProcessAuthorizeRequestAsync(NameValueCollection parameters, ClaimsPrincipal user, ConsentResponse consent) { if (user != null) { Logger.LogDebug("User in authorize request: {subjectId}", user.GetSubjectId()); } else { Logger.LogDebug("No user present in authorize request"); } // validate request var result = await _validator.ValidateAsync(parameters, user); if (result.IsError) { return await CreateErrorResultAsync( "Request validation failed", result.ValidatedRequest, result.Error, result.ErrorDescription); } var request = result.ValidatedRequest; LogRequest(request); // determine user interaction var interactionResult = await _interactionGenerator.ProcessInteractionAsync(request, consent); if (interactionResult.IsError) { return await CreateErrorResultAsync("Interaction generator error", request, interactionResult.Error, interactionResult.ErrorDescription, false); } if (interactionResult.IsLogin) { return new LoginPageResult(request); } if (interactionResult.IsConsent) { return new ConsentPageResult(request); } if (interactionResult.IsRedirect) { return new CustomRedirectResult(request, interactionResult.RedirectUrl); } var response = await _authorizeResponseGenerator.CreateResponseAsync(request); await RaiseResponseEventAsync(response); LogResponse(response); return new AuthorizeResult(response); } protected async Task CreateErrorResultAsync( string logMessage, ValidatedAuthorizeRequest request = null, string error = OidcConstants.AuthorizeErrors.ServerError, string errorDescription = null, bool logError = true) { if (logError) { Logger.LogError(logMessage); } if (request != null) { var details = new AuthorizeRequestValidationLog(request, _options.Logging.AuthorizeRequestSensitiveValuesFilter); Logger.LogInformation("{@validationDetails}", details); } // TODO: should we raise a token failure event for all errors to the authorize endpoint? await RaiseFailureEventAsync(request, error, errorDescription); return new AuthorizeResult(new AuthorizeResponse { Request = request, Error = error, ErrorDescription = errorDescription, SessionState = request?.GenerateSessionStateValue() }); } private void LogRequest(ValidatedAuthorizeRequest request) { var details = new AuthorizeRequestValidationLog(request, _options.Logging.AuthorizeRequestSensitiveValuesFilter); Logger.LogDebug(nameof(ValidatedAuthorizeRequest) + Environment.NewLine + "{@validationDetails}", details); } private void LogResponse(AuthorizeResponse response) { var details = new AuthorizeResponseLog(response); Logger.LogDebug("Authorize endpoint response" + Environment.NewLine + "{@details}", details); } private void LogTokens(AuthorizeResponse response) { var clientId = $"{response.Request.ClientId} ({response.Request.Client.ClientName ?? "no name set"})"; var subjectId = response.Request.Subject.GetSubjectId(); if (response.IdentityToken != null) { Logger.LogTrace("Identity token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.IdentityToken); } if (response.Code != null) { Logger.LogTrace("Code issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.Code); } if (response.AccessToken != null) { Logger.LogTrace("Access token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.AccessToken); } } private Task RaiseFailureEventAsync(ValidatedAuthorizeRequest request, string error, string errorDescription) { return _events.RaiseAsync(new TokenIssuedFailureEvent(request, error, errorDescription)); } private Task RaiseResponseEventAsync(AuthorizeResponse response) { if (!response.IsError) { LogTokens(response); return _events.RaiseAsync(new TokenIssuedSuccessEvent(response)); } return RaiseFailureEventAsync(response.Request, response.Error, response.ErrorDescription); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/CheckSessionEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; internal class CheckSessionEndpoint : IEndpointHandler { private readonly ILogger _logger; public CheckSessionEndpoint(ILogger logger) { _logger = logger; } public Task ProcessAsync(HttpContext context) { IEndpointResult result; if (!HttpMethods.IsGet(context.Request.Method)) { _logger.LogWarning("Invalid HTTP method for check session endpoint"); result = new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } else { _logger.LogDebug("Rendering check session result"); result = new CheckSessionResult(); } return Task.FromResult(result); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/DeviceAuthorizationEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; /// /// The device authorization endpoint /// /// internal class DeviceAuthorizationEndpoint : IEndpointHandler { private readonly IClientSecretValidator _clientValidator; private readonly IDeviceAuthorizationRequestValidator _requestValidator; private readonly IDeviceAuthorizationResponseGenerator _responseGenerator; private readonly IEventService _events; private readonly ILogger _logger; public DeviceAuthorizationEndpoint( IClientSecretValidator clientValidator, IDeviceAuthorizationRequestValidator requestValidator, IDeviceAuthorizationResponseGenerator responseGenerator, IEventService events, ILogger logger) { _clientValidator = clientValidator; _requestValidator = requestValidator; _responseGenerator = responseGenerator; _events = events; _logger = logger; } /// /// Processes the request. /// /// The HTTP context. /// /// public async Task ProcessAsync(HttpContext context) { _logger.LogTrace("Processing device authorize request."); // validate HTTP if (!HttpMethods.IsPost(context.Request.Method) || !context.Request.HasApplicationFormContentType()) { _logger.LogWarning("Invalid HTTP request for device authorize endpoint"); return Error(OidcConstants.TokenErrors.InvalidRequest); } return await ProcessDeviceAuthorizationRequestAsync(context); } private async Task ProcessDeviceAuthorizationRequestAsync(HttpContext context) { _logger.LogDebug("Start device authorize request."); // validate client var clientResult = await _clientValidator.ValidateAsync(context); if (clientResult.Client == null) return Error(OidcConstants.TokenErrors.InvalidClient); // validate request var form = (await context.Request.ReadFormAsync()).AsNameValueCollection(); var requestResult = await _requestValidator.ValidateAsync(form, clientResult); if (requestResult.IsError) { await _events.RaiseAsync(new DeviceAuthorizationFailureEvent(requestResult)); return Error(requestResult.Error, requestResult.ErrorDescription); } var baseUrl = context.GetIdentityServerBaseUrl().EnsureTrailingSlash(); // create response _logger.LogTrace("Calling into device authorize response generator: {type}", _responseGenerator.GetType().FullName); var response = await _responseGenerator.ProcessAsync(requestResult, baseUrl); await _events.RaiseAsync(new DeviceAuthorizationSuccessEvent(response, requestResult)); // return result _logger.LogDebug("Device authorize request success."); return new DeviceAuthorizationResult(response); } private TokenErrorResult Error(string error, string errorDescription = null, Dictionary custom = null) { var response = new TokenErrorResponse { Error = error, ErrorDescription = errorDescription, Custom = custom }; _logger.LogError("Device authorization error: {error}:{errorDescriptions}", error, error ?? "-no message-"); return new TokenErrorResult(response); } private void LogResponse(DeviceAuthorizationResponse response, DeviceAuthorizationRequestValidationResult requestResult) { var clientId = $"{requestResult.ValidatedRequest.Client.ClientId} ({requestResult.ValidatedRequest.Client?.ClientName ?? "no name set"})"; if (response.DeviceCode != null) { _logger.LogTrace("Device code issued for {clientId}: {deviceCode}", clientId, response.DeviceCode); } if (response.UserCode != null) { _logger.LogTrace("User code issued for {clientId}: {userCode}", clientId, response.UserCode); } if (response.VerificationUri != null) { _logger.LogTrace("Verification URI issued for {clientId}: {verificationUri}", clientId, response.VerificationUri); } if (response.VerificationUriComplete != null) { _logger.LogTrace("Verification URI (Complete) issued for {clientId}: {verificationUriComplete}", clientId, response.VerificationUriComplete); } } } ================================================ FILE: src/IdentityServer8/src/Endpoints/DiscoveryEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; internal class DiscoveryEndpoint : IEndpointHandler { private readonly ILogger _logger; private readonly IdentityServerOptions _options; private readonly IDiscoveryResponseGenerator _responseGenerator; public DiscoveryEndpoint( IdentityServerOptions options, IDiscoveryResponseGenerator responseGenerator, ILogger logger) { _logger = logger; _options = options; _responseGenerator = responseGenerator; } public async Task ProcessAsync(HttpContext context) { _logger.LogTrace("Processing discovery request."); // validate HTTP if (!HttpMethods.IsGet(context.Request.Method)) { _logger.LogWarning("Discovery endpoint only supports GET requests"); return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } _logger.LogDebug("Start discovery request"); if (!_options.Endpoints.EnableDiscoveryEndpoint) { _logger.LogInformation("Discovery endpoint disabled. 404."); return new StatusCodeResult(HttpStatusCode.NotFound); } var baseUrl = context.GetIdentityServerBaseUrl().EnsureTrailingSlash(); var issuerUri = context.GetIdentityServerIssuerUri(); // generate response _logger.LogTrace("Calling into discovery response generator: {type}", _responseGenerator.GetType().FullName); var response = await _responseGenerator.CreateDiscoveryDocumentAsync(baseUrl, issuerUri); return new DiscoveryDocumentResult(response, _options.Discovery.ResponseCacheInterval); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/DiscoveryKeyEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; internal class DiscoveryKeyEndpoint : IEndpointHandler { private readonly ILogger _logger; private readonly IdentityServerOptions _options; private readonly IDiscoveryResponseGenerator _responseGenerator; public DiscoveryKeyEndpoint( IdentityServerOptions options, IDiscoveryResponseGenerator responseGenerator, ILogger logger) { _logger = logger; _options = options; _responseGenerator = responseGenerator; } public async Task ProcessAsync(HttpContext context) { _logger.LogTrace("Processing discovery request."); // validate HTTP if (!HttpMethods.IsGet(context.Request.Method)) { _logger.LogWarning("Discovery endpoint only supports GET requests"); return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } _logger.LogDebug("Start key discovery request"); if (_options.Discovery.ShowKeySet == false) { _logger.LogInformation("Key discovery disabled. 404."); return new StatusCodeResult(HttpStatusCode.NotFound); } // generate response _logger.LogTrace("Calling into discovery response generator: {type}", _responseGenerator.GetType().FullName); var response = await _responseGenerator.CreateJwkDocumentAsync(); return new JsonWebKeysResult(response, _options.Discovery.ResponseCacheInterval); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/EndSessionCallbackEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; internal class EndSessionCallbackEndpoint : IEndpointHandler { private readonly IEndSessionRequestValidator _endSessionRequestValidator; private readonly ILogger _logger; public EndSessionCallbackEndpoint( IEndSessionRequestValidator endSessionRequestValidator, ILogger logger) { _endSessionRequestValidator = endSessionRequestValidator; _logger = logger; } public async Task ProcessAsync(HttpContext context) { if (!HttpMethods.IsGet(context.Request.Method)) { _logger.LogWarning("Invalid HTTP method for end session callback endpoint."); return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } _logger.LogDebug("Processing signout callback request"); var parameters = context.Request.Query.AsNameValueCollection(); var result = await _endSessionRequestValidator.ValidateCallbackAsync(parameters); if (!result.IsError) { _logger.LogInformation("Successful signout callback."); } else { _logger.LogError("Error validating signout callback: {error}", result.Error); } return new EndSessionCallbackResult(result); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/EndSessionEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; internal class EndSessionEndpoint : IEndpointHandler { private readonly IEndSessionRequestValidator _endSessionRequestValidator; private readonly ILogger _logger; private readonly IUserSession _userSession; public EndSessionEndpoint( IEndSessionRequestValidator endSessionRequestValidator, IUserSession userSession, ILogger logger) { _endSessionRequestValidator = endSessionRequestValidator; _userSession = userSession; _logger = logger; } public async Task ProcessAsync(HttpContext context) { NameValueCollection parameters; if (HttpMethods.IsGet(context.Request.Method)) { parameters = context.Request.Query.AsNameValueCollection(); } else if (HttpMethods.IsPost(context.Request.Method)) { parameters = (await context.Request.ReadFormAsync()).AsNameValueCollection(); } else { _logger.LogWarning("Invalid HTTP method for end session endpoint."); return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } var user = await _userSession.GetUserAsync(); _logger.LogDebug("Processing signout request for {subjectId}", user?.GetSubjectId() ?? "anonymous"); var result = await _endSessionRequestValidator.ValidateAsync(parameters, user); if (result.IsError) { _logger.LogError("Error processing end session request {error}", result.Error); } else { _logger.LogDebug("Success validating end session request from {clientId}", result.ValidatedRequest?.Client?.ClientId); } return new EndSessionResult(result); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/IntrospectionEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; /// /// Introspection endpoint /// /// internal class IntrospectionEndpoint : IEndpointHandler { private readonly IIntrospectionResponseGenerator _responseGenerator; private readonly IEventService _events; private readonly ILogger _logger; private readonly IIntrospectionRequestValidator _requestValidator; private readonly IApiSecretValidator _apiSecretValidator; /// /// Initializes a new instance of the class. /// /// The API secret validator. /// The request validator. /// The generator. /// The events. /// The logger. public IntrospectionEndpoint( IApiSecretValidator apiSecretValidator, IIntrospectionRequestValidator requestValidator, IIntrospectionResponseGenerator responseGenerator, IEventService events, ILogger logger) { _apiSecretValidator = apiSecretValidator; _requestValidator = requestValidator; _responseGenerator = responseGenerator; _events = events; _logger = logger; } /// /// Processes the request. /// /// The HTTP context. /// public async Task ProcessAsync(HttpContext context) { _logger.LogTrace("Processing introspection request."); // validate HTTP if (!HttpMethods.IsPost(context.Request.Method)) { _logger.LogWarning("Introspection endpoint only supports POST requests"); return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } if (!context.Request.HasApplicationFormContentType()) { _logger.LogWarning("Invalid media type for introspection endpoint"); return new StatusCodeResult(HttpStatusCode.UnsupportedMediaType); } return await ProcessIntrospectionRequestAsync(context); } private async Task ProcessIntrospectionRequestAsync(HttpContext context) { _logger.LogDebug("Starting introspection request."); // caller validation var apiResult = await _apiSecretValidator.ValidateAsync(context); if (apiResult.Resource == null) { _logger.LogError("API unauthorized to call introspection endpoint. aborting."); return new StatusCodeResult(HttpStatusCode.Unauthorized); } var body = await context.Request.ReadFormAsync(); if (body == null) { _logger.LogError("Malformed request body. aborting."); await _events.RaiseAsync(new TokenIntrospectionFailureEvent(apiResult.Resource.Name, "Malformed request body")); return new StatusCodeResult(HttpStatusCode.BadRequest); } // request validation _logger.LogTrace("Calling into introspection request validator: {type}", _requestValidator.GetType().FullName); var validationResult = await _requestValidator.ValidateAsync(body.AsNameValueCollection(), apiResult.Resource); if (validationResult.IsError) { LogFailure(validationResult.Error, apiResult.Resource.Name); await _events.RaiseAsync(new TokenIntrospectionFailureEvent(apiResult.Resource.Name, validationResult.Error)); return new BadRequestResult(validationResult.Error); } // response generation _logger.LogTrace("Calling into introspection response generator: {type}", _responseGenerator.GetType().FullName); var response = await _responseGenerator.ProcessAsync(validationResult); // render result LogSuccess(validationResult.IsActive, validationResult.Api.Name); return new IntrospectionResult(response); } private void LogSuccess(bool tokenActive, string apiName) { _logger.LogInformation("Success token introspection. Token active: {tokenActive}, for API name: {apiName}", tokenActive, apiName); } private void LogFailure(string error, string apiName) { _logger.LogError("Failed token introspection: {error}, for API name: {apiName}", error, apiName); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/AuthorizeResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; internal class AuthorizeResult : IEndpointResult { public AuthorizeResponse Response { get; } public AuthorizeResult(AuthorizeResponse response) { Response = response ?? throw new ArgumentNullException(nameof(response)); } internal AuthorizeResult( AuthorizeResponse response, IdentityServerOptions options, IUserSession userSession, IMessageStore errorMessageStore, ISystemClock clock) : this(response) { _options = options; _userSession = userSession; _errorMessageStore = errorMessageStore; _clock = clock; } private IdentityServerOptions _options; private IUserSession _userSession; private IMessageStore _errorMessageStore; private ISystemClock _clock; private void Init(HttpContext context) { _options = _options ?? context.RequestServices.GetRequiredService(); _userSession = _userSession ?? context.RequestServices.GetRequiredService(); _errorMessageStore = _errorMessageStore ?? context.RequestServices.GetRequiredService>(); _clock = _clock ?? context.RequestServices.GetRequiredService(); } public async Task ExecuteAsync(HttpContext context) { Init(context); if (Response.IsError) { await ProcessErrorAsync(context); } else { await ProcessResponseAsync(context); } } private async Task ProcessErrorAsync(HttpContext context) { // these are the conditions where we can send a response // back directly to the client, otherwise we're only showing the error UI var isSafeError = Response.Error == OidcConstants.AuthorizeErrors.AccessDenied || Response.Error == OidcConstants.AuthorizeErrors.AccountSelectionRequired || Response.Error == OidcConstants.AuthorizeErrors.LoginRequired || Response.Error == OidcConstants.AuthorizeErrors.ConsentRequired || Response.Error == OidcConstants.AuthorizeErrors.InteractionRequired; if (isSafeError) { // this scenario we can return back to the client await ProcessResponseAsync(context); } else { // we now know we must show error page await RedirectToErrorPageAsync(context); } } protected async Task ProcessResponseAsync(HttpContext context) { if (!Response.IsError) { // success response -- track client authorization for sign-out //_logger.LogDebug("Adding client {0} to client list cookie for subject {1}", request.ClientId, request.Subject.GetSubjectId()); await _userSession.AddClientIdAsync(Response.Request.ClientId); } await RenderAuthorizeResponseAsync(context); } private async Task RenderAuthorizeResponseAsync(HttpContext context) { if (Response.Request.ResponseMode == OidcConstants.ResponseModes.Query || Response.Request.ResponseMode == OidcConstants.ResponseModes.Fragment) { context.Response.SetNoCache(); context.Response.Redirect(BuildRedirectUri()); } else if (Response.Request.ResponseMode == OidcConstants.ResponseModes.FormPost) { context.Response.SetNoCache(); AddSecurityHeaders(context); await context.Response.WriteHtmlAsync(GetFormPostHtml()); } else { //_logger.LogError("Unsupported response mode."); throw new InvalidOperationException("Unsupported response mode"); } } private void AddSecurityHeaders(HttpContext context) { context.Response.AddScriptCspHeaders(_options.Csp, "sha256-orD0/VhH8hLqrLxKHD/HUEMdwqX6/0ve7c5hspX5VJ8="); var referrer_policy = "no-referrer"; if (!context.Response.Headers.ContainsKey("Referrer-Policy")) { context.Response.Headers.Append("Referrer-Policy", referrer_policy); } } private string BuildRedirectUri() { var uri = Response.RedirectUri; var query = Response.ToNameValueCollection().ToQueryString(); if (Response.Request.ResponseMode == OidcConstants.ResponseModes.Query) { uri = uri.AddQueryString(query); } else { uri = uri.AddHashFragment(query); } if (Response.IsError && !uri.Contains("#")) { // https://tools.ietf.org/html/draft-bradley-oauth-open-redirector-00 uri += "#_=_"; } return uri; } private const string FormPostHtml = "
    {body}
    "; private string GetFormPostHtml() { var html = FormPostHtml; var url = Response.Request.RedirectUri; url = HtmlEncoder.Default.Encode(url); html = html.Replace("{uri}", url); html = html.Replace("{body}", Response.ToNameValueCollection().ToFormPost()); return html; } private async Task RedirectToErrorPageAsync(HttpContext context) { var errorModel = new ErrorMessage { RequestId = context.TraceIdentifier, Error = Response.Error, ErrorDescription = Response.ErrorDescription, UiLocales = Response.Request?.UiLocales, DisplayMode = Response.Request?.DisplayMode, ClientId = Response.Request?.ClientId }; if (Response.RedirectUri != null && Response.Request?.ResponseMode != null) { // if we have a valid redirect uri, then include it to the error page errorModel.RedirectUri = BuildRedirectUri(); errorModel.ResponseMode = Response.Request.ResponseMode; } var message = new Message(errorModel, _clock.UtcNow.UtcDateTime); var id = await _errorMessageStore.WriteAsync(message); var errorUrl = _options.UserInteraction.ErrorUrl; var url = errorUrl.AddQueryString(_options.UserInteraction.ErrorIdParameter, id); context.Response.RedirectToAbsoluteUrl(url); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/BadRequestResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; internal class BadRequestResult : IEndpointResult { public string Error { get; set; } public string ErrorDescription { get; set; } public BadRequestResult(string error = null, string errorDescription = null) { Error = error; ErrorDescription = errorDescription; } public async Task ExecuteAsync(HttpContext context) { context.Response.StatusCode = 400; context.Response.SetNoCache(); if (Error.IsPresent()) { var dto = new ResultDto { error = Error, error_description = ErrorDescription }; await context.Response.WriteJsonAsync(dto); } } internal class ResultDto { public string error { get; set; } public string error_description { get; set; } } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/CheckSessionResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; internal class CheckSessionResult : IEndpointResult { public CheckSessionResult() { } internal CheckSessionResult(IdentityServerOptions options) { _options = options; } private IdentityServerOptions _options; private static volatile string FormattedHtml; private static readonly object Lock = new object(); private static volatile string LastCheckSessionCookieName; private void Init(HttpContext context) { _options = _options ?? context.RequestServices.GetRequiredService(); } public async Task ExecuteAsync(HttpContext context) { Init(context); AddCspHeaders(context); var html = GetHtml(_options.Authentication.CheckSessionCookieName); await context.Response.WriteHtmlAsync(html); } private void AddCspHeaders(HttpContext context) { context.Response.AddScriptCspHeaders(_options.Csp, "sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI="); } private string GetHtml(string cookieName) { if (cookieName != LastCheckSessionCookieName) { lock (Lock) { if (cookieName != LastCheckSessionCookieName) { FormattedHtml = Html.Replace("{cookieName}", cookieName); LastCheckSessionCookieName = cookieName; } } } return FormattedHtml; } private const string Html = @" Check Session IFrame "; } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/ConsentPageResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for consent page /// /// public class ConsentPageResult : IEndpointResult { private readonly ValidatedAuthorizeRequest _request; /// /// Initializes a new instance of the class. /// /// The request. /// request public ConsentPageResult(ValidatedAuthorizeRequest request) { _request = request ?? throw new ArgumentNullException(nameof(request)); } internal ConsentPageResult( ValidatedAuthorizeRequest request, IdentityServerOptions options, IAuthorizationParametersMessageStore authorizationParametersMessageStore = null) : this(request) { _options = options; _authorizationParametersMessageStore = authorizationParametersMessageStore; } private IdentityServerOptions _options; private IAuthorizationParametersMessageStore _authorizationParametersMessageStore; private void Init(HttpContext context) { _options = _options ?? context.RequestServices.GetRequiredService(); _authorizationParametersMessageStore = _authorizationParametersMessageStore ?? context.RequestServices.GetService(); } /// /// Executes the result. /// /// The HTTP context. /// public async Task ExecuteAsync(HttpContext context) { Init(context); var returnUrl = context.GetIdentityServerBasePath().EnsureTrailingSlash() + Constants.ProtocolRoutePaths.AuthorizeCallback; if (_authorizationParametersMessageStore != null) { var msg = new Message>(_request.Raw.ToFullDictionary()); var id = await _authorizationParametersMessageStore.WriteAsync(msg); returnUrl = returnUrl.AddQueryString(Constants.AuthorizationParamsStore.MessageStoreIdParameterName, id); } else { returnUrl = returnUrl.AddQueryString(_request.Raw.ToQueryString()); } var consentUrl = _options.UserInteraction.ConsentUrl; if (!consentUrl.IsLocalUrl()) { // this converts the relative redirect path to an absolute one if we're // redirecting to a different server returnUrl = context.GetIdentityServerHost().EnsureTrailingSlash() + returnUrl.RemoveLeadingSlash(); } var url = consentUrl.AddQueryString(_options.UserInteraction.ConsentReturnUrlParameter, returnUrl); context.Response.RedirectToAbsoluteUrl(url); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/CustomRedirectResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for a custom redirect /// /// public class CustomRedirectResult : IEndpointResult { private readonly ValidatedAuthorizeRequest _request; private readonly string _url; /// /// Initializes a new instance of the class. /// /// The request. /// The URL. /// /// request /// or /// url /// public CustomRedirectResult(ValidatedAuthorizeRequest request, string url) { if (request == null) throw new ArgumentNullException(nameof(request)); if (url.IsMissing()) throw new ArgumentNullException(nameof(url)); _request = request; _url = url; } internal CustomRedirectResult( ValidatedAuthorizeRequest request, string url, IdentityServerOptions options) : this(request, url) { _options = options; } private IdentityServerOptions _options; private void Init(HttpContext context) { _options = _options ?? context.RequestServices.GetRequiredService(); } /// /// Executes the result. /// /// The HTTP context. /// public Task ExecuteAsync(HttpContext context) { Init(context); var returnUrl = context.GetIdentityServerBasePath().EnsureTrailingSlash() + Constants.ProtocolRoutePaths.Authorize; returnUrl = returnUrl.AddQueryString(_request.Raw.ToQueryString()); if (!_url.IsLocalUrl()) { // this converts the relative redirect path to an absolute one if we're // redirecting to a different server returnUrl = context.GetIdentityServerBaseUrl().EnsureTrailingSlash() + returnUrl.RemoveLeadingSlash(); } var url = _url.AddQueryString(_options.UserInteraction.CustomRedirectReturnUrlParameter, returnUrl); context.Response.RedirectToAbsoluteUrl(url); return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/DeviceAuthorizationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; internal class DeviceAuthorizationResult : IEndpointResult { public DeviceAuthorizationResponse Response { get; } public DeviceAuthorizationResult(DeviceAuthorizationResponse response) { Response = response ?? throw new ArgumentNullException(nameof(response)); } public async Task ExecuteAsync(HttpContext context) { context.Response.SetNoCache(); var dto = new ResultDto { device_code = Response.DeviceCode, user_code = Response.UserCode, verification_uri = Response.VerificationUri, verification_uri_complete = Response.VerificationUriComplete, expires_in = Response.DeviceCodeLifetime, interval = Response.Interval }; await context.Response.WriteJsonAsync(dto); } internal class ResultDto { public string device_code { get; set; } public string user_code { get; set; } public string verification_uri { get; set; } public string verification_uri_complete { get; set; } public int expires_in { get; set; } public int interval { get; set; } } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/DiscoveryDocumentResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for a discovery document /// /// public class DiscoveryDocumentResult : IEndpointResult { /// /// Gets the entries. /// /// /// The entries. /// public Dictionary Entries { get; } /// /// Gets the maximum age. /// /// /// The maximum age. /// public int? MaxAge { get; } /// /// Initializes a new instance of the class. /// /// The entries. /// The maximum age. /// entries public DiscoveryDocumentResult(Dictionary entries, int? maxAge) { Entries = entries ?? throw new ArgumentNullException(nameof(entries)); MaxAge = maxAge; } /// /// Executes the result. /// /// The HTTP context. /// public Task ExecuteAsync(HttpContext context) { if (MaxAge.HasValue && MaxAge.Value >= 0) { context.Response.SetCache(MaxAge.Value, "Origin"); } return context.Response.WriteJsonAsync(Entries); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/EndSessionCallbackResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; internal class EndSessionCallbackResult : IEndpointResult { private readonly EndSessionCallbackValidationResult _result; public EndSessionCallbackResult(EndSessionCallbackValidationResult result) { _result = result ?? throw new ArgumentNullException(nameof(result)); } internal EndSessionCallbackResult( EndSessionCallbackValidationResult result, IdentityServerOptions options) : this(result) { _options = options; } private IdentityServerOptions _options; private void Init(HttpContext context) { _options = _options ?? context.RequestServices.GetRequiredService(); } public async Task ExecuteAsync(HttpContext context) { Init(context); if (_result.IsError) { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; } else { context.Response.SetNoCache(); AddCspHeaders(context); var html = GetHtml(); await context.Response.WriteHtmlAsync(html); } } private void AddCspHeaders(HttpContext context) { if (_options.Authentication.RequireCspFrameSrcForSignout) { string frameSources = null; var origins = _result.FrontChannelLogoutUrls?.Select(x => x.GetOrigin()); if (origins != null && origins.Any()) { frameSources = origins.Distinct().Aggregate((x, y) => $"{x} {y}"); } // the hash matches the embedded style element being used below context.Response.AddStyleCspHeaders(_options.Csp, "sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY=", frameSources); } } private string GetHtml() { string framesHtml = null; if (_result.FrontChannelLogoutUrls != null && _result.FrontChannelLogoutUrls.Any()) { var frameUrls = _result.FrontChannelLogoutUrls.Select(url => $""); framesHtml = frameUrls.Aggregate((x, y) => x + y); } return $"{framesHtml}"; } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/EndSessionResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for endsession /// /// public class EndSessionResult : IEndpointResult { private readonly EndSessionValidationResult _result; /// /// Initializes a new instance of the class. /// /// The result. /// result public EndSessionResult(EndSessionValidationResult result) { _result = result ?? throw new ArgumentNullException(nameof(result)); } internal EndSessionResult( EndSessionValidationResult result, IdentityServerOptions options, ISystemClock clock, IMessageStore logoutMessageStore) : this(result) { _options = options; _clock = clock; _logoutMessageStore = logoutMessageStore; } private IdentityServerOptions _options; private ISystemClock _clock; private IMessageStore _logoutMessageStore; private void Init(HttpContext context) { _options = _options ?? context.RequestServices.GetRequiredService(); _clock = _clock ?? context.RequestServices.GetRequiredService(); _logoutMessageStore = _logoutMessageStore ?? context.RequestServices.GetRequiredService>(); } /// /// Executes the result. /// /// The HTTP context. /// public async Task ExecuteAsync(HttpContext context) { Init(context); var validatedRequest = _result.IsError ? null : _result.ValidatedRequest; string id = null; if (validatedRequest != null) { var logoutMessage = new LogoutMessage(validatedRequest); if (logoutMessage.ContainsPayload) { var msg = new Message(logoutMessage, _clock.UtcNow.UtcDateTime); id = await _logoutMessageStore.WriteAsync(msg); } } var redirect = _options.UserInteraction.LogoutUrl; if (redirect.IsLocalUrl()) { redirect = context.GetIdentityServerRelativeUrl(redirect); } if (id != null) { redirect = redirect.AddQueryString(_options.UserInteraction.LogoutIdParameter, id); } context.Response.RedirectIfAllowed(redirect); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/IntrospectionResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for introspection /// /// public class IntrospectionResult : IEndpointResult { /// /// Gets the result. /// /// /// The result. /// public Dictionary Entries { get; } /// /// Initializes a new instance of the class. /// /// The result. /// result public IntrospectionResult(Dictionary entries) { Entries = entries ?? throw new ArgumentNullException(nameof(entries)); } /// /// Executes the result. /// /// The HTTP context. /// public Task ExecuteAsync(HttpContext context) { context.Response.SetNoCache(); return context.Response.WriteJsonAsync(Entries); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/JsonWebKeysResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for the jwks document /// /// public class JsonWebKeysResult : IEndpointResult { /// /// Gets the web keys. /// /// /// The web keys. /// public IEnumerable WebKeys { get; } /// /// Gets the maximum age. /// /// /// The maximum age. /// public int? MaxAge { get; } /// /// Initializes a new instance of the class. /// /// The web keys. /// The maximum age. public JsonWebKeysResult(IEnumerable webKeys, int? maxAge) { WebKeys = webKeys ?? throw new ArgumentNullException(nameof(webKeys)); MaxAge = maxAge; } /// /// Executes the result. /// /// The HTTP context. /// public Task ExecuteAsync(HttpContext context) { if (MaxAge.HasValue && MaxAge.Value >= 0) { context.Response.SetCache(MaxAge.Value, "Origin"); } return context.Response.WriteJsonAsync(new { keys = WebKeys }, "application/json; charset=UTF-8"); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/LoginPageResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for login page /// /// public class LoginPageResult : IEndpointResult { private readonly ValidatedAuthorizeRequest _request; /// /// Initializes a new instance of the class. /// /// The request. /// request public LoginPageResult(ValidatedAuthorizeRequest request) { _request = request ?? throw new ArgumentNullException(nameof(request)); } internal LoginPageResult( ValidatedAuthorizeRequest request, IdentityServerOptions options, IAuthorizationParametersMessageStore authorizationParametersMessageStore = null) : this(request) { _options = options; _authorizationParametersMessageStore = authorizationParametersMessageStore; } private IdentityServerOptions _options; private IAuthorizationParametersMessageStore _authorizationParametersMessageStore; private void Init(HttpContext context) { _options = _options ?? context.RequestServices.GetRequiredService(); _authorizationParametersMessageStore = _authorizationParametersMessageStore ?? context.RequestServices.GetService(); } /// /// Executes the result. /// /// The HTTP context. /// public async Task ExecuteAsync(HttpContext context) { Init(context); var returnUrl = context.GetIdentityServerBasePath().EnsureTrailingSlash() + Constants.ProtocolRoutePaths.AuthorizeCallback; if (_authorizationParametersMessageStore != null) { var msg = new Message>(_request.Raw.ToFullDictionary()); var id = await _authorizationParametersMessageStore.WriteAsync(msg); returnUrl = returnUrl.AddQueryString(Constants.AuthorizationParamsStore.MessageStoreIdParameterName, id); } else { returnUrl = returnUrl.AddQueryString(_request.Raw.ToQueryString()); } var loginUrl = _options.UserInteraction.LoginUrl; if (!loginUrl.IsLocalUrl()) { // this converts the relative redirect path to an absolute one if we're // redirecting to a different server returnUrl = context.GetIdentityServerHost().EnsureTrailingSlash() + returnUrl.RemoveLeadingSlash(); } var url = loginUrl.AddQueryString(_options.UserInteraction.LoginReturnUrlParameter, returnUrl); context.Response.RedirectToAbsoluteUrl(url); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/ProtectedResourceErrorResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.Net.Http.Headers; namespace IdentityServer8.Endpoints.Results; internal class ProtectedResourceErrorResult : IEndpointResult { public string Error; public string ErrorDescription; public ProtectedResourceErrorResult(string error, string errorDescription = null) { Error = error; ErrorDescription = errorDescription; } public Task ExecuteAsync(HttpContext context) { context.Response.StatusCode = 401; context.Response.SetNoCache(); if (Constants.ProtectedResourceErrorStatusCodes.ContainsKey(Error)) { context.Response.StatusCode = Constants.ProtectedResourceErrorStatusCodes[Error]; } if (Error == OidcConstants.ProtectedResourceErrors.ExpiredToken) { Error = OidcConstants.ProtectedResourceErrors.InvalidToken; ErrorDescription = "The access token expired"; } var errorString = string.Format($"error=\"{Error}\""); if (ErrorDescription.IsMissing()) { context.Response.Headers.Append(HeaderNames.WWWAuthenticate, new StringValues(new[] { "Bearer realm=\"IdentityServer\"", errorString }).ToString()); } else { var errorDescriptionString = string.Format($"error_description=\"{ErrorDescription}\""); context.Response.Headers.Append(HeaderNames.WWWAuthenticate, new StringValues(new[] { "Bearer realm=\"IdentityServer\"", errorString, errorDescriptionString }).ToString()); } return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/StatusCodeResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for a raw HTTP status code /// /// public class StatusCodeResult : IEndpointResult { /// /// Gets the status code. /// /// /// The status code. /// public int StatusCode { get; } /// /// Initializes a new instance of the class. /// /// The status code. public StatusCodeResult(HttpStatusCode statusCode) { StatusCode = (int)statusCode; } /// /// Initializes a new instance of the class. /// /// The status code. public StatusCodeResult(int statusCode) { StatusCode = statusCode; } /// /// Executes the result. /// /// The HTTP context. /// public Task ExecuteAsync(HttpContext context) { context.Response.StatusCode = StatusCode; return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/TokenErrorResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using JsonExtensionDataAttribute = System.Text.Json.Serialization.JsonExtensionDataAttribute; namespace IdentityServer8.Endpoints.Results; internal class TokenErrorResult : IEndpointResult { public TokenErrorResponse Response { get; } public TokenErrorResult(TokenErrorResponse error) { if (error.Error.IsMissing()) throw new ArgumentNullException(nameof(error.Error), "Error must be set"); Response = error; } public async Task ExecuteAsync(HttpContext context) { context.Response.StatusCode = 400; context.Response.SetNoCache(); var dto = new ResultDto { error = Response.Error, error_description = Response.ErrorDescription, custom = Response.Custom }; await context.Response.WriteJsonAsync(dto); } internal class ResultDto { public string error { get; set; } public string error_description { get; set; } [JsonExtensionData] public Dictionary custom { get; set; } } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/TokenResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using JsonExtensionDataAttribute = System.Text.Json.Serialization.JsonExtensionDataAttribute; using JsonSerializer = System.Text.Json.JsonSerializer; namespace IdentityServer8.Endpoints.Results; internal class TokenResult : IEndpointResult { public TokenResponse Response { get; set; } public TokenResult(TokenResponse response) { Response = response ?? throw new ArgumentNullException(nameof(response)); } public async Task ExecuteAsync(HttpContext context) { context.Response.SetNoCache(); var dto = new ResultDto { id_token = Response.IdentityToken, access_token = Response.AccessToken, refresh_token = Response.RefreshToken, expires_in = Response.AccessTokenLifetime, token_type = OidcConstants.TokenResponse.BearerTokenType, scope = Response.Scope, Custom = Response.Custom }; await context.Response.WriteJsonAsync(dto); } internal class ResultDto { public string id_token { get; set; } public string access_token { get; set; } public int expires_in { get; set; } public string token_type { get; set; } public string refresh_token { get; set; } public string scope { get; set; } [JsonExtensionData] public Dictionary? Custom { get; set; } } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/TokenRevocationErrorResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; /// /// Result for revocation error /// /// public class TokenRevocationErrorResult : IEndpointResult { /// /// Gets or sets the error. /// /// /// The error. /// public string Error { get; set; } /// /// Initializes a new instance of the class. /// /// The error. public TokenRevocationErrorResult(string error) { Error = error; } /// /// Executes the result. /// /// The HTTP context. /// public Task ExecuteAsync(HttpContext context) { context.Response.StatusCode = (int)HttpStatusCode.BadRequest; return context.Response.WriteJsonAsync(new { error = Error }); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/Results/UserInfoResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints.Results; internal class UserInfoResult : IEndpointResult { public Dictionary Claims; public UserInfoResult(Dictionary claims) { Claims = claims; } public async Task ExecuteAsync(HttpContext context) { context.Response.SetNoCache(); await context.Response.WriteJsonAsync(Claims); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/TokenEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; /// /// The token endpoint /// /// internal class TokenEndpoint : IEndpointHandler { private readonly IClientSecretValidator _clientValidator; private readonly ITokenRequestValidator _requestValidator; private readonly ITokenResponseGenerator _responseGenerator; private readonly IEventService _events; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The client validator. /// The request validator. /// The response generator. /// The events. /// The logger. public TokenEndpoint( IClientSecretValidator clientValidator, ITokenRequestValidator requestValidator, ITokenResponseGenerator responseGenerator, IEventService events, ILogger logger) { _clientValidator = clientValidator; _requestValidator = requestValidator; _responseGenerator = responseGenerator; _events = events; _logger = logger; } /// /// Processes the request. /// /// The HTTP context. /// public async Task ProcessAsync(HttpContext context) { _logger.LogTrace("Processing token request."); // validate HTTP if (!HttpMethods.IsPost(context.Request.Method) || !context.Request.HasApplicationFormContentType()) { _logger.LogWarning("Invalid HTTP request for token endpoint"); return Error(OidcConstants.TokenErrors.InvalidRequest); } return await ProcessTokenRequestAsync(context); } private async Task ProcessTokenRequestAsync(HttpContext context) { _logger.LogDebug("Start token request."); // validate client var clientResult = await _clientValidator.ValidateAsync(context); if (clientResult.Client == null) { return Error(OidcConstants.TokenErrors.InvalidClient); } // validate request var form = (await context.Request.ReadFormAsync()).AsNameValueCollection(); _logger.LogTrace("Calling into token request validator: {type}", _requestValidator.GetType().FullName); var requestResult = await _requestValidator.ValidateRequestAsync(form, clientResult); if (requestResult.IsError) { await _events.RaiseAsync(new TokenIssuedFailureEvent(requestResult)); return Error(requestResult.Error, requestResult.ErrorDescription, requestResult.CustomResponse); } // create response _logger.LogTrace("Calling into token request response generator: {type}", _responseGenerator.GetType().FullName); var response = await _responseGenerator.ProcessAsync(requestResult); await _events.RaiseAsync(new TokenIssuedSuccessEvent(response, requestResult)); LogTokens(response, requestResult); // return result _logger.LogDebug("Token request success."); return new TokenResult(response); } private TokenErrorResult Error(string error, string errorDescription = null, Dictionary custom = null) { var response = new TokenErrorResponse { Error = error, ErrorDescription = errorDescription, Custom = custom }; return new TokenErrorResult(response); } private void LogTokens(TokenResponse response, TokenRequestValidationResult requestResult) { var clientId = $"{requestResult.ValidatedRequest.Client.ClientId} ({requestResult.ValidatedRequest.Client?.ClientName ?? "no name set"})"; var subjectId = requestResult.ValidatedRequest.Subject?.GetSubjectId() ?? "no subject"; if (response.IdentityToken != null) { _logger.LogTrace("Identity token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.IdentityToken); } if (response.RefreshToken != null) { _logger.LogTrace("Refresh token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.RefreshToken); } if (response.AccessToken != null) { _logger.LogTrace("Access token issued for {clientId} / {subjectId}: {token}", clientId, subjectId, response.AccessToken); } } } ================================================ FILE: src/IdentityServer8/src/Endpoints/TokenRevocationEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; /// /// The revocation endpoint /// /// internal class TokenRevocationEndpoint : IEndpointHandler { private readonly ILogger _logger; private readonly IClientSecretValidator _clientValidator; private readonly ITokenRevocationRequestValidator _requestValidator; private readonly ITokenRevocationResponseGenerator _responseGenerator; private readonly IEventService _events; /// /// Initializes a new instance of the class. /// /// The logger. /// The client validator. /// The request validator. /// The response generator. /// The events. public TokenRevocationEndpoint(ILogger logger, IClientSecretValidator clientValidator, ITokenRevocationRequestValidator requestValidator, ITokenRevocationResponseGenerator responseGenerator, IEventService events) { _logger = logger; _clientValidator = clientValidator; _requestValidator = requestValidator; _responseGenerator = responseGenerator; _events = events; } /// /// Processes the request. /// /// The HTTP context. /// public async Task ProcessAsync(HttpContext context) { _logger.LogTrace("Processing revocation request."); if (!HttpMethods.IsPost(context.Request.Method)) { _logger.LogWarning("Invalid HTTP method"); return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } if (!context.Request.HasApplicationFormContentType()) { _logger.LogWarning("Invalid media type"); return new StatusCodeResult(HttpStatusCode.UnsupportedMediaType); } var response = await ProcessRevocationRequestAsync(context); return response; } private async Task ProcessRevocationRequestAsync(HttpContext context) { _logger.LogDebug("Start revocation request."); // validate client var clientValidationResult = await _clientValidator.ValidateAsync(context); if (clientValidationResult.IsError) { return new TokenRevocationErrorResult(OidcConstants.TokenErrors.InvalidClient); } _logger.LogTrace("Client validation successful"); // validate the token request var form = (await context.Request.ReadFormAsync()).AsNameValueCollection(); _logger.LogTrace("Calling into token revocation request validator: {type}", _requestValidator.GetType().FullName); var requestValidationResult = await _requestValidator.ValidateRequestAsync(form, clientValidationResult.Client); if (requestValidationResult.IsError) { return new TokenRevocationErrorResult(requestValidationResult.Error); } _logger.LogTrace("Calling into token revocation response generator: {type}", _responseGenerator.GetType().FullName); var response = await _responseGenerator.ProcessAsync(requestValidationResult); if (response.Success) { _logger.LogInformation("Token revocation complete"); await _events.RaiseAsync(new TokenRevokedSuccessEvent(requestValidationResult, requestValidationResult.Client)); } else { _logger.LogInformation("No matching token found"); } if (response.Error.IsPresent()) return new TokenRevocationErrorResult(response.Error); return new StatusCodeResult(HttpStatusCode.OK); } } ================================================ FILE: src/IdentityServer8/src/Endpoints/UserInfoEndpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Endpoints; /// /// The userinfo endpoint /// /// internal class UserInfoEndpoint : IEndpointHandler { private readonly BearerTokenUsageValidator _tokenUsageValidator; private readonly IUserInfoRequestValidator _requestValidator; private readonly IUserInfoResponseGenerator _responseGenerator; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The token usage validator. /// The request validator. /// The response generator. /// The logger. public UserInfoEndpoint( BearerTokenUsageValidator tokenUsageValidator, IUserInfoRequestValidator requestValidator, IUserInfoResponseGenerator responseGenerator, ILogger logger) { _tokenUsageValidator = tokenUsageValidator; _requestValidator = requestValidator; _responseGenerator = responseGenerator; _logger = logger; } /// /// Processes the request. /// /// The HTTP context. /// public async Task ProcessAsync(HttpContext context) { if (!HttpMethods.IsGet(context.Request.Method) && !HttpMethods.IsPost(context.Request.Method)) { _logger.LogWarning("Invalid HTTP method for userinfo endpoint."); return new StatusCodeResult(HttpStatusCode.MethodNotAllowed); } return await ProcessUserInfoRequestAsync(context); } private async Task ProcessUserInfoRequestAsync(HttpContext context) { _logger.LogDebug("Start userinfo request"); // userinfo requires an access token on the request var tokenUsageResult = await _tokenUsageValidator.ValidateAsync(context); if (tokenUsageResult.TokenFound == false) { var error = "No access token found."; _logger.LogError(error); return Error(OidcConstants.ProtectedResourceErrors.InvalidToken); } // validate the request _logger.LogTrace("Calling into userinfo request validator: {type}", _requestValidator.GetType().FullName); var validationResult = await _requestValidator.ValidateRequestAsync(tokenUsageResult.Token); if (validationResult.IsError) { //_logger.LogError("Error validating validationResult.Error); return Error(validationResult.Error); } // generate response _logger.LogTrace("Calling into userinfo response generator: {type}", _responseGenerator.GetType().FullName); var response = await _responseGenerator.ProcessAsync(validationResult); _logger.LogDebug("End userinfo request"); return new UserInfoResult(response); } private IEndpointResult Error(string error, string description = null) { return new ProtectedResourceErrorResult(error, description); } } ================================================ FILE: src/IdentityServer8/src/Events/ApiAuthenticationFailureEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for failed API authentication /// /// public class ApiAuthenticationFailureEvent : Event { /// /// Initializes a new instance of the class. /// /// Name of the API. /// The message. public ApiAuthenticationFailureEvent(string apiName, string message) : base(EventCategories.Authentication, "API Authentication Failure", EventTypes.Failure, EventIds.ApiAuthenticationFailure, message) { ApiName = apiName; } /// /// Gets or sets the name of the API. /// /// /// The name of the API. /// public string ApiName { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/ApiAuthenticationSuccessEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for successful API authentication /// /// public class ApiAuthenticationSuccessEvent : Event { /// /// Initializes a new instance of the class. /// /// Name of the API. /// The authentication method. public ApiAuthenticationSuccessEvent(string apiName, string authenticationMethod) : base(EventCategories.Authentication, "API Authentication Success", EventTypes.Success, EventIds.ApiAuthenticationSuccess) { ApiName = apiName; AuthenticationMethod = authenticationMethod; } /// /// Gets or sets the name of the API. /// /// /// The name of the API. /// public string ApiName { get; set; } /// /// Gets or sets the authentication method. /// /// /// The authentication method. /// public string AuthenticationMethod { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/ClientAuthenticationFailureEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for failed client authentication /// /// public class ClientAuthenticationFailureEvent : Event { /// /// Initializes a new instance of the class. /// /// The client identifier. /// The message. public ClientAuthenticationFailureEvent(string clientId, string message) : base(EventCategories.Authentication, "Client Authentication Failure", EventTypes.Failure, EventIds.ClientAuthenticationFailure, message) { ClientId = clientId; } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/ClientAuthenticationSuccessEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for successful client authentication /// /// public class ClientAuthenticationSuccessEvent : Event { /// /// Initializes a new instance of the class. /// /// The client identifier. /// The authentication method. public ClientAuthenticationSuccessEvent(string clientId, string authenticationMethod) : base(EventCategories.Authentication, "Client Authentication Success", EventTypes.Success, EventIds.ClientAuthenticationSuccess) { ClientId = clientId; AuthenticationMethod = authenticationMethod; } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the authentication method. /// /// /// The authentication method. /// public string AuthenticationMethod { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/ConsentDeniedEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for denied consent. /// /// public class ConsentDeniedEvent : Event { /// /// Initializes a new instance of the class. /// /// The subject identifier. /// The client identifier. /// The requested scopes. public ConsentDeniedEvent(string subjectId, string clientId, IEnumerable requestedScopes) : base(EventCategories.Grants, "Consent denied", EventTypes.Information, EventIds.ConsentDenied) { SubjectId = subjectId; ClientId = clientId; RequestedScopes = requestedScopes; } /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the client ID. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the requested scopes. /// /// /// The requested scopes. /// public IEnumerable RequestedScopes { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/ConsentGrantedEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for granted consent. /// /// public class ConsentGrantedEvent : Event { /// /// Initializes a new instance of the class. /// /// The subject identifier. /// The client identifier. /// The requested scopes. /// The granted scopes. /// if set to true consent was remembered. public ConsentGrantedEvent(string subjectId, string clientId, IEnumerable requestedScopes, IEnumerable grantedScopes, bool consentRemembered) : base(EventCategories.Grants, "Consent granted", EventTypes.Information, EventIds.ConsentGranted) { SubjectId = subjectId; ClientId = clientId; RequestedScopes = requestedScopes; GrantedScopes = grantedScopes; ConsentRemembered = consentRemembered; } /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the client ID. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the requested scopes. /// /// /// The requested scopes. /// public IEnumerable RequestedScopes { get; set; } /// /// Gets or sets the granted scopes. /// /// /// The granted scopes. /// public IEnumerable GrantedScopes { get; set; } /// /// Gets or sets a value indicating whether consent was remembered. /// /// /// true if consent was remembered; otherwise, false. /// public bool ConsentRemembered { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/DeviceAuthorizationFailureEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for device authorization failure /// /// public class DeviceAuthorizationFailureEvent : Event { /// /// Initializes a new instance of the class. /// /// The result. public DeviceAuthorizationFailureEvent(DeviceAuthorizationRequestValidationResult result) : this() { if (result.ValidatedRequest != null) { ClientId = result.ValidatedRequest.Client?.ClientId; ClientName = result.ValidatedRequest.Client?.ClientName; Scopes = result.ValidatedRequest.RequestedScopes?.ToSpaceSeparatedString(); } Endpoint = Constants.EndpointNames.DeviceAuthorization; Error = result.Error; ErrorDescription = result.ErrorDescription; } /// /// Initializes a new instance of the class. /// public DeviceAuthorizationFailureEvent() : base(EventCategories.DeviceFlow, "Device Authorization Failure", EventTypes.Failure, EventIds.DeviceAuthorizationFailure) { } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the name of the client. /// /// /// The name of the client. /// public string ClientName { get; set; } /// /// Gets or sets the endpoint. /// /// /// The endpoint. /// public string Endpoint { get; set; } /// /// Gets or sets the scopes. /// /// /// The scopes. /// public string Scopes { get; set; } /// /// Gets or sets the error. /// /// /// The error. /// public string Error { get; set; } /// /// Gets or sets the error description. /// /// /// The error description. /// public string ErrorDescription { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/DeviceAuthorizationSuccessEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for device authorization failure /// /// public class DeviceAuthorizationSuccessEvent : Event { /// /// Initializes a new instance of the class. /// /// The response. /// The request. public DeviceAuthorizationSuccessEvent(DeviceAuthorizationResponse response, DeviceAuthorizationRequestValidationResult request) : this() { ClientId = request.ValidatedRequest.Client?.ClientId; ClientName = request.ValidatedRequest.Client?.ClientName; Endpoint = Constants.EndpointNames.DeviceAuthorization; Scopes = request.ValidatedRequest.ValidatedResources?.RawScopeValues.ToSpaceSeparatedString(); } /// /// Initializes a new instance of the class. /// protected DeviceAuthorizationSuccessEvent() : base(EventCategories.DeviceFlow, "Device Authorization Success", EventTypes.Success, EventIds.DeviceAuthorizationSuccess) { } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the name of the client. /// /// /// The name of the client. /// public string ClientName { get; set; } /// /// Gets or sets the endpoint. /// /// /// The endpoint. /// public string Endpoint { get; set; } /// /// Gets or sets the scopes. /// /// /// The scopes. /// public string Scopes { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/GrantsRevokedEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for revoked grants. /// /// public class GrantsRevokedEvent : Event { /// /// Initializes a new instance of the class. /// /// The subject identifier. /// The client identifier. public GrantsRevokedEvent(string subjectId, string clientId) : base(EventCategories.Grants, "Grants revoked", EventTypes.Information, EventIds.GrantsRevoked) { SubjectId = subjectId; ClientId = clientId; } /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the client ID. /// /// /// The client identifier. /// public string ClientId { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/Infrastructure/Event.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Models base class for events raised from IdentityServer. /// public abstract class Event { /// /// Initializes a new instance of the class. /// /// The category. /// The name. /// The type. /// The identifier. /// The message. /// category protected Event(string category, string name, EventTypes type, int id, string message = null) { Category = category ?? throw new ArgumentNullException(nameof(category)); Name = name ?? throw new ArgumentNullException(nameof(name)); EventType = type; Id = id; Message = message; } /// /// Allows implementing custom initialization logic. /// /// protected internal virtual Task PrepareAsync() { return Task.CompletedTask; } /// /// Gets or sets the category. /// /// /// The category. /// public string Category { get; set; } /// /// Gets or sets the name. /// /// /// The name. /// public string Name { get; set; } /// /// Gets or sets the event type. /// /// /// The type of the event. /// public EventTypes EventType { get; set; } /// /// Gets or sets the identifier. /// /// /// The identifier. /// public int Id { get; set; } /// /// Gets or sets the event message. /// /// /// The message. /// public string Message { get; set; } /// /// Gets or sets the per-request activity identifier. /// /// /// The activity identifier. /// public string ActivityId { get; set; } /// /// Gets or sets the time stamp when the event was raised. /// /// /// The time stamp. /// public DateTime TimeStamp { get; set; } /// /// Gets or sets the server process identifier. /// /// /// The process identifier. /// public int ProcessId { get; set; } /// /// Gets or sets the local ip address of the current request. /// /// /// The local ip address. /// public string LocalIpAddress { get; set; } /// /// Gets or sets the remote ip address of the current request. /// /// /// The remote ip address. /// public string RemoteIpAddress { get; set; } /// /// Obfuscates a token. /// /// The token. /// protected static string Obfuscate(string value) { var last4Chars = "****"; if (value.IsPresent() && value.Length > 4) { last4Chars = value.Substring(value.Length - 4); } return "****" + last4Chars; } /// public override string ToString() { return LogSerializer.Serialize(this); } } ================================================ FILE: src/IdentityServer8/src/Events/Infrastructure/EventCategories.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Categories for events /// public static class EventCategories { /// /// Authentication related events /// public const string Authentication = "Authentication"; /// /// Token related events /// public const string Token = "Token"; /// /// Grants related events /// public const string Grants = "Grants"; /// /// Error related events /// public const string Error = "Error"; /// /// Device flow related events /// public const string DeviceFlow = "Device"; } ================================================ FILE: src/IdentityServer8/src/Events/Infrastructure/EventIds.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Events; public static class EventIds { ////////////////////////////////////////////////////// /// Authentication related events ////////////////////////////////////////////////////// private const int AuthenticationEventsStart = 1000; public const int UserLoginSuccess = AuthenticationEventsStart + 0; public const int UserLoginFailure = AuthenticationEventsStart + 1; public const int UserLogoutSuccess = AuthenticationEventsStart + 2; public const int ClientAuthenticationSuccess = AuthenticationEventsStart + 10; public const int ClientAuthenticationFailure = AuthenticationEventsStart + 11; public const int ApiAuthenticationSuccess = AuthenticationEventsStart + 20; public const int ApiAuthenticationFailure = AuthenticationEventsStart + 21; ////////////////////////////////////////////////////// /// Token related events ////////////////////////////////////////////////////// private const int TokenEventsStart = 2000; public const int TokenIssuedSuccess = TokenEventsStart + 0; public const int TokenIssuedFailure = TokenEventsStart + 1; public const int TokenRevokedSuccess = TokenEventsStart + 10; public const int TokenIntrospectionSuccess = TokenEventsStart + 20; public const int TokenIntrospectionFailure = TokenEventsStart + 21; ////////////////////////////////////////////////////// /// Error related events ////////////////////////////////////////////////////// private const int ErrorEventsStart = 3000; public const int UnhandledException = ErrorEventsStart + 0; public const int InvalidClientConfiguration = ErrorEventsStart + 1; ////////////////////////////////////////////////////// /// Grants related events ////////////////////////////////////////////////////// private const int GrantsEventsStart = 4000; public const int ConsentGranted = GrantsEventsStart + 0; public const int ConsentDenied = GrantsEventsStart + 1; public const int GrantsRevoked = GrantsEventsStart + 2; private const int DeviceFlowEventsStart = 5000; public const int DeviceAuthorizationSuccess = DeviceFlowEventsStart + 0; public const int DeviceAuthorizationFailure = DeviceFlowEventsStart + 1; } ================================================ FILE: src/IdentityServer8/src/Events/Infrastructure/EventType.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Indicates if the event is a success or fail event. /// public enum EventTypes { /// /// Success event /// Success = 1, /// /// Failure event /// Failure = 2, /// /// Information event /// Information = 3, /// /// Error event /// Error = 4 } ================================================ FILE: src/IdentityServer8/src/Events/InvalidClientConfiguration.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for unhandled exceptions /// /// public class InvalidClientConfigurationEvent : Event { /// /// Initializes a new instance of the class. /// /// The client. /// The error message. public InvalidClientConfigurationEvent(Client client, string errorMessage) : base(EventCategories.Error, "Invalid Client Configuration", EventTypes.Error, EventIds.InvalidClientConfiguration, errorMessage) { ClientId = client.ClientId; ClientName = client.ClientName ?? "unknown name"; } /// /// Gets or sets the client ID. /// /// /// The details. /// public string ClientId { get; set; } /// /// Gets or sets the name of the client. /// /// /// The name of the client. /// public string ClientName { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/TokenIntrospectionFailureEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for failed token introspection /// /// public class TokenIntrospectionFailureEvent : Event { /// /// Initializes a new instance of the class. /// /// Name of the API. /// The error message. /// The token. /// The API scopes. /// The token scopes. public TokenIntrospectionFailureEvent(string apiName, string errorMessage, string token = null, IEnumerable apiScopes = null, IEnumerable tokenScopes = null) : base(EventCategories.Token, "Token Introspection Failure", EventTypes.Failure, EventIds.TokenIntrospectionFailure, errorMessage) { ApiName = apiName; if (token.IsPresent()) { Token = Obfuscate(token); } if (apiScopes != null) { ApiScopes = apiScopes; } if (tokenScopes != null) { TokenScopes = tokenScopes; } } /// /// Gets or sets the name of the API. /// /// /// The name of the API. /// public string ApiName { get; set; } /// /// Gets or sets the token. /// /// /// The token. /// public string Token { get; set; } /// /// Gets or sets the API scopes. /// /// /// The API scopes. /// public IEnumerable ApiScopes { get; set; } /// /// Gets or sets the token scopes. /// /// /// The token scopes. /// public IEnumerable TokenScopes { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/TokenIntrospectionSuccessEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for successful token introspection /// /// public class TokenIntrospectionSuccessEvent : Event { /// /// Initializes a new instance of the class. /// /// The result. public TokenIntrospectionSuccessEvent(IntrospectionRequestValidationResult result) : base(EventCategories.Token, "Token Introspection Success", EventTypes.Success, EventIds.TokenIntrospectionSuccess) { ApiName = result.Api.Name; IsActive = result.IsActive; if (result.Token.IsPresent()) { Token = Obfuscate(result.Token); } if (!result.Claims.EnumerableIsNullOrEmpty()) { ClaimTypes = result.Claims.Select(c => c.Type).Distinct(); TokenScopes = result.Claims.Where(c => c.Type == "scope").Select(c => c.Value); } } /// /// Gets or sets the name of the API. /// /// /// The name of the API. /// public string ApiName { get; set; } /// /// Gets or sets a value indicating whether this instance is active. /// /// /// true if this instance is active; otherwise, false. /// public bool IsActive { get; set; } /// /// Gets or sets the token. /// /// /// The token. /// public string Token { get; set; } /// /// Gets or sets the claim types. /// /// /// The claim types. /// public IEnumerable ClaimTypes { get; set; } /// /// Gets or sets the token scopes. /// /// /// The token scopes. /// public IEnumerable TokenScopes { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/TokenIssuedFailureEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for failed token issuance /// /// public class TokenIssuedFailureEvent : Event { /// /// Initializes a new instance of the class. /// /// The request. /// The error. /// The description. public TokenIssuedFailureEvent(ValidatedAuthorizeRequest request, string error, string description) : this() { if (request != null) { ClientId = request.ClientId; ClientName = request.Client?.ClientName; RedirectUri = request.RedirectUri; Scopes = request.RequestedScopes?.ToSpaceSeparatedString(); GrantType = request.GrantType; if (request.Subject != null && request.Subject.Identity.IsAuthenticated) { SubjectId = request.Subject?.GetSubjectId(); } } Endpoint = EndpointNames.Authorize; Error = error; ErrorDescription = description; } /// /// Initializes a new instance of the class. /// /// The result. public TokenIssuedFailureEvent(TokenRequestValidationResult result) : this() { if (result.ValidatedRequest != null) { ClientId = result.ValidatedRequest.Client.ClientId; ClientName = result.ValidatedRequest.Client.ClientName; GrantType = result.ValidatedRequest.GrantType; Scopes = result.ValidatedRequest.RequestedScopes?.ToSpaceSeparatedString(); if (result.ValidatedRequest.Subject != null && result.ValidatedRequest.Subject.Identity.IsAuthenticated) { SubjectId = result.ValidatedRequest.Subject.GetSubjectId(); } } Endpoint = EndpointNames.Token; Error = result.Error; ErrorDescription = result.ErrorDescription; } /// /// Initializes a new instance of the class. /// protected TokenIssuedFailureEvent() : base(EventCategories.Token, "Token Issued Failure", EventTypes.Failure, EventIds.TokenIssuedFailure) { } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the name of the client. /// /// /// The name of the client. /// public string ClientName { get; set; } /// /// Gets or sets the redirect URI. /// /// /// The redirect URI. /// public string RedirectUri { get; set; } /// /// Gets or sets the endpoint. /// /// /// The endpoint. /// public string Endpoint { get; set; } /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the scopes. /// /// /// The scopes. /// public string Scopes { get; set; } /// /// Gets or sets the grant type. /// /// /// The grant type. /// public string GrantType { get; set; } /// /// Gets or sets the error. /// /// /// The error. /// public string Error { get; set; } /// /// Gets or sets the error description. /// /// /// The error description. /// public string ErrorDescription { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/TokenIssuedSuccessEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for successful token issuance /// /// public class TokenIssuedSuccessEvent : Event { /// /// Initializes a new instance of the class. /// /// The response. public TokenIssuedSuccessEvent(AuthorizeResponse response) : this() { ClientId = response.Request.ClientId; ClientName = response.Request.Client.ClientName; RedirectUri = response.RedirectUri; Endpoint = EndpointNames.Authorize; SubjectId = response.Request.Subject.GetSubjectId(); Scopes = response.Scope; GrantType = response.Request.GrantType; var tokens = new List(); if (response.IdentityToken != null) { tokens.Add(new Token(OidcConstants.TokenTypes.IdentityToken, response.IdentityToken)); } if (response.Code != null) { tokens.Add(new Token(OidcConstants.ResponseTypes.Code, response.Code)); } if (response.AccessToken != null) { tokens.Add(new Token(OidcConstants.TokenTypes.AccessToken, response.AccessToken)); } Tokens = tokens; } /// /// Initializes a new instance of the class. /// /// The response. /// The request. public TokenIssuedSuccessEvent(TokenResponse response, TokenRequestValidationResult request) : this() { ClientId = request.ValidatedRequest.Client.ClientId; ClientName = request.ValidatedRequest.Client.ClientName; Endpoint = EndpointNames.Token; SubjectId = request.ValidatedRequest.Subject?.GetSubjectId(); GrantType = request.ValidatedRequest.GrantType; if (GrantType == OidcConstants.GrantTypes.RefreshToken) { Scopes = request.ValidatedRequest.RefreshToken.AccessToken.Scopes.ToSpaceSeparatedString(); } else if (GrantType == OidcConstants.GrantTypes.AuthorizationCode) { Scopes = request.ValidatedRequest.AuthorizationCode.RequestedScopes.ToSpaceSeparatedString(); } else { Scopes = request.ValidatedRequest.ValidatedResources?.RawScopeValues.ToSpaceSeparatedString(); } var tokens = new List(); if (response.IdentityToken != null) { tokens.Add(new Token(OidcConstants.TokenTypes.IdentityToken, response.IdentityToken)); } if (response.RefreshToken != null) { tokens.Add(new Token(OidcConstants.TokenTypes.RefreshToken, response.RefreshToken)); } if (response.AccessToken != null) { tokens.Add(new Token(OidcConstants.TokenTypes.AccessToken, response.AccessToken)); } Tokens = tokens; } /// /// Initializes a new instance of the class. /// protected TokenIssuedSuccessEvent() : base(EventCategories.Token, "Token Issued Success", EventTypes.Success, EventIds.TokenIssuedSuccess) { } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the name of the client. /// /// /// The name of the client. /// public string ClientName { get; set; } /// /// Gets or sets the redirect URI. /// /// /// The redirect URI. /// public string RedirectUri { get; set; } /// /// Gets or sets the endpoint. /// /// /// The endpoint. /// public string Endpoint { get; set; } /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the scopes. /// /// /// The scopes. /// public string Scopes { get; set; } /// /// Gets or sets the grant type. /// /// /// The grant type. /// public string GrantType { get; set; } /// /// Gets or sets the tokens. /// /// /// The tokens. /// public IEnumerable Tokens { get; set; } /// /// Data structure serializing issued tokens /// public class Token { /// /// Initializes a new instance of the class. /// /// The type. /// The value. public Token(string type, string value) { TokenType = type; TokenValue = Obfuscate(value); } /// /// Gets the type of the token. /// /// /// The type of the token. /// public string TokenType { get; } /// /// Gets the token value. /// /// /// The token value. /// public string TokenValue { get; } } } ================================================ FILE: src/IdentityServer8/src/Events/TokenRevokedSuccessEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for successful token revocation /// /// public class TokenRevokedSuccessEvent : Event { /// /// Initializes a new instance of the class. /// /// The request result. /// The client. public TokenRevokedSuccessEvent(TokenRevocationRequestValidationResult requestResult, Client client) : base(EventCategories.Token, "Token Revoked Success", EventTypes.Success, EventIds.TokenRevokedSuccess) { ClientId = client.ClientId; ClientName = client.ClientName; TokenType = requestResult.TokenTypeHint; Token = Obfuscate(requestResult.Token); } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the name of the client. /// /// /// The name of the client. /// public string ClientName { get; set; } /// /// Gets or sets the type of the token. /// /// /// The type of the token. /// public string TokenType { get; set; } /// /// Gets or sets the token. /// /// /// The token. /// public string Token { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/UnhandledExceptionEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for unhandled exceptions /// /// public class UnhandledExceptionEvent : Event { /// /// Initializes a new instance of the class. /// /// The ex. public UnhandledExceptionEvent(Exception ex) : base(EventCategories.Error, "Unhandled Exception", EventTypes.Error, EventIds.UnhandledException, ex.Message) { Details = ex.ToString(); } /// /// Gets or sets the details. /// /// /// The details. /// public string Details { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/UserLoginFailureEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for failed user authentication /// /// public class UserLoginFailureEvent : Event { /// /// Initializes a new instance of the class. /// /// The username. /// The error. /// Specifies if login was interactive /// The client id public UserLoginFailureEvent(string username, string error, bool interactive = true, string clientId = null) : base(EventCategories.Authentication, "User Login Failure", EventTypes.Failure, EventIds.UserLoginFailure, error) { Username = username; ClientId = clientId; if (interactive) { Endpoint = "UI"; } else { Endpoint = EndpointNames.Token; } } /// /// Gets or sets the username. /// /// /// The username. /// public string Username { get; set; } /// /// Gets or sets the endpoint. /// /// /// The endpoint. /// public string Endpoint { get; set; } /// /// Gets or sets the client id. /// /// /// The client id. /// public string ClientId { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/UserLoginSuccessEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for successful user authentication /// /// public class UserLoginSuccessEvent : Event { // todo: consolidate ctors in 3.0 /// /// Initializes a new instance of the class. /// /// The provider. /// The provider user identifier. /// The subject identifier. /// The name. /// if set to true [interactive]. /// The client id. public UserLoginSuccessEvent(string provider, string providerUserId, string subjectId, string name, bool interactive = true, string clientId = null) : this() { Provider = provider; ProviderUserId = providerUserId; SubjectId = subjectId; DisplayName = name; if (interactive) { Endpoint = "UI"; } else { Endpoint = EndpointNames.Token; } ClientId = clientId; } /// /// Initializes a new instance of the class. /// /// The username. /// The subject identifier. /// The name. /// if set to true [interactive]. /// The client id. public UserLoginSuccessEvent(string username, string subjectId, string name, bool interactive = true, string clientId = null) : this() { Username = username; SubjectId = subjectId; DisplayName = name; ClientId = clientId; if (interactive) { Endpoint = "UI"; } else { Endpoint = EndpointNames.Token; } } /// /// Initializes a new instance of the class. /// protected UserLoginSuccessEvent() : base(EventCategories.Authentication, "User Login Success", EventTypes.Success, EventIds.UserLoginSuccess) { } /// /// Gets or sets the username. /// /// /// The username. /// public string Username { get; set; } /// /// Gets or sets the provider. /// /// /// The provider. /// public string Provider { get; set; } /// /// Gets or sets the provider user identifier. /// /// /// The provider user identifier. /// public string ProviderUserId { get; set; } /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the display name. /// /// /// The display name. /// public string DisplayName { get; set; } /// /// Gets or sets the endpoint. /// /// /// The endpoint. /// public string Endpoint { get; set; } /// /// Gets or sets the client id. /// /// /// The client id. /// public string ClientId { get; set; } } ================================================ FILE: src/IdentityServer8/src/Events/UserLogoutSuccessEvent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// Event for successful user logout /// /// public class UserLogoutSuccessEvent : Event { /// /// Initializes a new instance of the class. /// /// The subject identifier. /// The name. public UserLogoutSuccessEvent(string subjectId, string name) : base(EventCategories.Authentication, "User Logout Success", EventTypes.Success, EventIds.UserLogoutSuccess) { SubjectId = subjectId; DisplayName = name; } /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the display name. /// /// /// The display name. /// public string DisplayName { get; set; } } ================================================ FILE: src/IdentityServer8/src/Extensions/AuthenticationPropertiesExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; /// /// Extensions for AuthenticationProperties /// public static class AuthenticationPropertiesExtensions { internal const string SessionIdKey = "session_id"; internal const string ClientListKey = "client_list"; /// /// Gets the user's session identifier. /// /// /// public static string GetSessionId(this AuthenticationProperties properties) { if (properties?.Items.ContainsKey(SessionIdKey) == true) { return properties.Items[SessionIdKey]; } return null; } /// /// Sets the user's session identifier. /// /// /// The session id /// public static void SetSessionId(this AuthenticationProperties properties, string sid) { properties.Items[SessionIdKey] = sid; } /// /// Gets the list of client ids the user has signed into during their session. /// /// /// public static IEnumerable GetClientList(this AuthenticationProperties properties) { if (properties?.Items.ContainsKey(ClientListKey) == true) { var value = properties.Items[ClientListKey]; return DecodeList(value); } return Enumerable.Empty(); } /// /// Removes the list of client ids. /// /// public static void RemoveClientList(this AuthenticationProperties properties) { properties?.Items.Remove(ClientListKey); } /// /// Adds a client to the list of clients the user has signed into during their session. /// /// /// public static void AddClientId(this AuthenticationProperties properties, string clientId) { if (clientId == null) throw new ArgumentNullException(nameof(clientId)); var clients = properties.GetClientList(); if (!clients.Contains(clientId)) { var update = clients.ToList(); update.Add(clientId); var value = EncodeList(update); if (value == null) { properties.Items.Remove(ClientListKey); } else { properties.Items[ClientListKey] = value; } } } private static IEnumerable DecodeList(string value) { if (value.IsPresent()) { var bytes = Base64Url.Decode(value); value = Encoding.UTF8.GetString(bytes); return ObjectSerializer.FromString(value); } return Enumerable.Empty(); } private static string EncodeList(IEnumerable list) { if (list != null && list.Any()) { var value = ObjectSerializer.ToString(list); var bytes = Encoding.UTF8.GetBytes(value); value = Base64Url.Encode(bytes); return value; } return null; } } ================================================ FILE: src/IdentityServer8/src/Extensions/AuthorizeResponseExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; internal static class AuthorizeResponseExtensions { public static NameValueCollection ToNameValueCollection(this AuthorizeResponse response) { var collection = new NameValueCollection(); if (response.IsError) { if (response.Error.IsPresent()) { collection.Add("error", response.Error); } if (response.ErrorDescription.IsPresent()) { collection.Add("error_description", response.ErrorDescription); } } else { if (response.Code.IsPresent()) { collection.Add("code", response.Code); } if (response.IdentityToken.IsPresent()) { collection.Add("id_token", response.IdentityToken); } if (response.AccessToken.IsPresent()) { collection.Add("access_token", response.AccessToken); collection.Add("token_type", "Bearer"); collection.Add("expires_in", response.AccessTokenLifetime.ToString()); } if (response.Scope.IsPresent()) { collection.Add("scope", response.Scope); } } if (response.State.IsPresent()) { collection.Add("state", response.State); } if (response.SessionState.IsPresent()) { collection.Add("session_state", response.SessionState); } return collection; } } ================================================ FILE: src/IdentityServer8/src/Extensions/ClaimsExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; internal static class ClaimsExtensions { public static Dictionary ToClaimsDictionary(this IEnumerable claims) { var d = new Dictionary(); if (claims == null) { return d; } var distinctClaims = claims.Distinct(new ClaimComparer()); foreach (var claim in distinctClaims) { if (!d.ContainsKey(claim.Type)) { d.Add(claim.Type, GetValue(claim)); } else { var value = d[claim.Type]; if (value is List list) { list.Add(GetValue(claim)); } else { d.Remove(claim.Type); d.Add(claim.Type, new List { value, GetValue(claim) }); } } } return d; } private static object GetValue(Claim claim) { if (claim.ValueType == ClaimValueTypes.Integer || claim.ValueType == ClaimValueTypes.Integer32) { if (Int32.TryParse(claim.Value, out int value)) { return value; } } if (claim.ValueType == ClaimValueTypes.Integer64) { if (Int64.TryParse(claim.Value, out long value)) { return value; } } if (claim.ValueType == ClaimValueTypes.Boolean) { if (bool.TryParse(claim.Value, out bool value)) { return value; } } if (claim.ValueType == IdentityServerConstants.ClaimValueTypes.Json) { try { return System.Text.Json.JsonSerializer.Deserialize(claim.Value); } catch { } } return claim.Value; } } ================================================ FILE: src/IdentityServer8/src/Extensions/ClientExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Models; /// /// Extension methods for client. /// public static class ClientExtensions { /// /// Returns true if the client is an implicit-only client. /// public static bool IsImplicitOnly(this Client client) { return client != null && client.AllowedGrantTypes != null && client.AllowedGrantTypes.Count == 1 && client.AllowedGrantTypes.First() == GrantType.Implicit; } /// /// Constructs a list of SecurityKey from a Secret collection /// /// The secrets /// public static Task> GetKeysAsync(this IEnumerable secrets) { var secretList = secrets.ToList().AsReadOnly(); var keys = new List(); var certificates = GetCertificates(secretList) .Select(c => (SecurityKey)new X509SecurityKey(c)) .ToList(); keys.AddRange(certificates); var jwks = secretList .Where(s => s.Type == IdentityServerConstants.SecretTypes.JsonWebKey) .Select(s => new Microsoft.IdentityModel.Tokens.JsonWebKey(s.Value)) .ToList(); keys.AddRange(jwks); return Task.FromResult(keys); } private static List GetCertificates(IEnumerable secrets) { return secrets .Where(s => s.Type == IdentityServerConstants.SecretTypes.X509CertificateBase64) .Select(s => new X509Certificate2(Convert.FromBase64String(s.Value))) .Where(c => c != null) .ToList(); } } ================================================ FILE: src/IdentityServer8/src/Extensions/DateTimeExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; internal static class DateTimeExtensions { [DebuggerStepThrough] public static bool HasExceeded(this DateTime creationTime, int seconds, DateTime now) { return (now > creationTime.AddSeconds(seconds)); } [DebuggerStepThrough] public static int GetLifetimeInSeconds(this DateTime creationTime, DateTime now) { return ((int)(now - creationTime).TotalSeconds); } [DebuggerStepThrough] public static bool HasExpired(this DateTime? expirationTime, DateTime now) { if (expirationTime.HasValue && expirationTime.Value.HasExpired(now)) { return true; } return false; } [DebuggerStepThrough] public static bool HasExpired(this DateTime expirationTime, DateTime now) { if (now > expirationTime) { return true; } return false; } } ================================================ FILE: src/IdentityServer8/src/Extensions/EndpointOptionsExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Endpoint = IdentityServer8.Hosting.Endpoint; namespace IdentityServer8.Extensions; internal static class EndpointOptionsExtensions { public static bool IsEndpointEnabled(this EndpointsOptions options, Endpoint endpoint) { return endpoint?.Name switch { EndpointNames.Authorize => options.EnableAuthorizeEndpoint, EndpointNames.CheckSession => options.EnableCheckSessionEndpoint, EndpointNames.DeviceAuthorization => options.EnableDeviceAuthorizationEndpoint, EndpointNames.Discovery => options.EnableDiscoveryEndpoint, EndpointNames.EndSession => options.EnableEndSessionEndpoint, EndpointNames.Introspection => options.EnableIntrospectionEndpoint, EndpointNames.Revocation => options.EnableTokenRevocationEndpoint, EndpointNames.Token => options.EnableTokenEndpoint, EndpointNames.UserInfo => options.EnableUserInfoEndpoint, _ => true }; } } ================================================ FILE: src/IdentityServer8/src/Extensions/HashExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Extension methods for hashing strings /// public static class HashExtensions { /// /// Creates a SHA256 hash of the specified input. /// /// The input. /// A hash public static string Sha256(this string input) { if (input.IsMissing()) return string.Empty; using (var sha = SHA256.Create()) { var bytes = Encoding.UTF8.GetBytes(input); var hash = sha.ComputeHash(bytes); return Convert.ToBase64String(hash); } } /// /// Creates a SHA256 hash of the specified input. /// /// The input. /// A hash. public static byte[] Sha256(this byte[] input) { if (input == null) { return null; } using (var sha = SHA256.Create()) { return sha.ComputeHash(input); } } /// /// Creates a SHA512 hash of the specified input. /// /// The input. /// A hash public static string Sha512(this string input) { if (input.IsMissing()) return string.Empty; using (var sha = SHA512.Create()) { var bytes = Encoding.UTF8.GetBytes(input); var hash = sha.ComputeHash(bytes); return Convert.ToBase64String(hash); } } } ================================================ FILE: src/IdentityServer8/src/Extensions/HttpContextAuthenticationExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.AspNetCore.Http; /// /// Extension methods for signin/out using the IdentityServer authentication scheme. /// public static class AuthenticationManagerExtensions { /// /// Signs the user in. /// /// The manager. /// The IdentityServer user. /// public static async Task SignInAsync(this HttpContext context, IdentityServerUser user) { await context.SignInAsync(await context.GetCookieAuthenticationSchemeAsync(), user.CreatePrincipal()); } /// /// Signs the user in. /// /// The manager. /// The IdentityServer user. /// The authentication properties. /// public static async Task SignInAsync(this HttpContext context, IdentityServerUser user, AuthenticationProperties properties) { await context.SignInAsync(await context.GetCookieAuthenticationSchemeAsync(), user.CreatePrincipal(), properties); } internal static ISystemClock GetClock(this HttpContext context) { return context.RequestServices.GetRequiredService(); } internal static async Task GetCookieAuthenticationSchemeAsync(this HttpContext context) { var options = context.RequestServices.GetRequiredService(); if (options.Authentication.CookieAuthenticationScheme != null) { return options.Authentication.CookieAuthenticationScheme; } var schemes = context.RequestServices.GetRequiredService(); var scheme = await schemes.GetDefaultAuthenticateSchemeAsync(); if (scheme == null) { throw new InvalidOperationException("No DefaultAuthenticateScheme found or no CookieAuthenticationScheme configured on IdentityServerOptions."); } return scheme.Name; } } ================================================ FILE: src/IdentityServer8/src/Extensions/HttpContextExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Extensions; public static class HttpContextExtensions { public static async Task GetSchemeSupportsSignOutAsync(this HttpContext context, string scheme) { var provider = context.RequestServices.GetRequiredService(); var handler = await provider.GetHandlerAsync(context, scheme); return (handler is IAuthenticationSignOutHandler); } public static void SetIdentityServerOrigin(this HttpContext context, string value) { if (context == null) throw new ArgumentNullException(nameof(context)); if (value == null) throw new ArgumentNullException(nameof(value)); var split = value.Split(new[] { "://" }, StringSplitOptions.RemoveEmptyEntries); var request = context.Request; request.Scheme = split.First(); request.Host = new HostString(split.Last()); } public static void SetIdentityServerBasePath(this HttpContext context, string value) { if (context == null) throw new ArgumentNullException(nameof(context)); context.Items[Constants.EnvironmentKeys.IdentityServerBasePath] = value; } public static string GetIdentityServerOrigin(this HttpContext context) { var options = context.RequestServices.GetRequiredService(); var request = context.Request; if (options.MutualTls.Enabled && options.MutualTls.DomainName.IsPresent()) { if (!options.MutualTls.DomainName.Contains(".")) { if (request.Host.Value.StartsWith(options.MutualTls.DomainName, StringComparison.OrdinalIgnoreCase)) { return request.Scheme + "://" + request.Host.Value.Substring(options.MutualTls.DomainName.Length + 1); } } } return request.Scheme + "://" + request.Host.Value; } internal static void SetSignOutCalled(this HttpContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); context.Items[Constants.EnvironmentKeys.SignOutCalled] = "true"; } internal static bool GetSignOutCalled(this HttpContext context) { return context.Items.ContainsKey(Constants.EnvironmentKeys.SignOutCalled); } /// /// Gets the host name of IdentityServer. /// /// The context. /// public static string GetIdentityServerHost(this HttpContext context) { var request = context.Request; return request.Scheme + "://" + request.Host.ToUriComponent(); } /// /// Gets the base path of IdentityServer. /// /// The context. /// public static string GetIdentityServerBasePath(this HttpContext context) { return context.Items[Constants.EnvironmentKeys.IdentityServerBasePath] as string; } /// /// Gets the public base URL for IdentityServer. /// /// The context. /// public static string GetIdentityServerBaseUrl(this HttpContext context) { return context.GetIdentityServerHost() + context.GetIdentityServerBasePath(); } /// /// Gets the identity server relative URL. /// /// The context. /// The path. /// public static string GetIdentityServerRelativeUrl(this HttpContext context, string path) { if (!path.IsLocalUrl()) { return null; } if (path.StartsWith("~/")) path = path.Substring(1); path = context.GetIdentityServerBaseUrl().EnsureTrailingSlash() + path.RemoveLeadingSlash(); return path; } /// /// Gets the identity server issuer URI. /// /// The context. /// /// context public static string GetIdentityServerIssuerUri(this HttpContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); // if they've explicitly configured a URI then use it, // otherwise dynamically calculate it var options = context.RequestServices.GetRequiredService(); var uri = options.IssuerUri; if (uri.IsMissing()) { uri = context.GetIdentityServerOrigin() + context.GetIdentityServerBasePath(); if (uri.EndsWith("/")) uri = uri.Substring(0, uri.Length - 1); if (options.LowerCaseIssuerUri) { uri = uri.ToLowerInvariant(); } } return uri; } internal static async Task GetIdentityServerSignoutFrameCallbackUrlAsync(this HttpContext context, LogoutMessage logoutMessage = null) { var userSession = context.RequestServices.GetRequiredService(); var user = await userSession.GetUserAsync(); var currentSubId = user?.GetSubjectId(); LogoutNotificationContext endSessionMsg = null; // if we have a logout message, then that take precedence over the current user if (logoutMessage?.ClientIds?.Any() == true) { var clientIds = logoutMessage?.ClientIds; // check if current user is same, since we might have new clients (albeit unlikely) if (currentSubId == logoutMessage?.SubjectId) { clientIds = clientIds.Union(await userSession.GetClientListAsync()); clientIds = clientIds.Distinct(); } endSessionMsg = new LogoutNotificationContext { SubjectId = logoutMessage.SubjectId, SessionId = logoutMessage.SessionId, ClientIds = clientIds }; } else if (currentSubId != null) { // see if current user has any clients they need to signout of var clientIds = await userSession.GetClientListAsync(); if (clientIds.Any()) { endSessionMsg = new LogoutNotificationContext { SubjectId = currentSubId, SessionId = await userSession.GetSessionIdAsync(), ClientIds = clientIds }; } } if (endSessionMsg != null) { var clock = context.RequestServices.GetRequiredService(); var msg = new Message(endSessionMsg, clock.UtcNow.UtcDateTime); var endSessionMessageStore = context.RequestServices.GetRequiredService>(); var id = await endSessionMessageStore.WriteAsync(msg); var signoutIframeUrl = context.GetIdentityServerBaseUrl().EnsureTrailingSlash() + Constants.ProtocolRoutePaths.EndSessionCallback; signoutIframeUrl = signoutIframeUrl.AddQueryString(Constants.UIConstants.DefaultRoutePathParams.EndSessionCallback, id); return signoutIframeUrl; } // no sessions, so nothing to cleanup return null; } } ================================================ FILE: src/IdentityServer8/src/Extensions/HttpRequestExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 using System.Net.Http.Headers; namespace IdentityServer8.Extensions; public static class HttpRequestExtensions { public static string GetCorsOrigin(this HttpRequest request) { var origin = request.Headers["Origin"].FirstOrDefault(); var thisOrigin = request.Scheme + "://" + request.Host; // see if the Origin is different than this server's origin. if so // that indicates a proper CORS request. some browsers send Origin // on POST requests. if (origin != null && origin != thisOrigin) { return origin; } return null; } internal static bool HasApplicationFormContentType(this HttpRequest request) { if (request.ContentType is null) return false; if (MediaTypeHeaderValue.TryParse(request.ContentType, out var header)) { // Content-Type: application/x-www-form-urlencoded; charset=utf-8 return header.MediaType.Equals("application/x-www-form-urlencoded", StringComparison.OrdinalIgnoreCase); } return false; } } ================================================ FILE: src/IdentityServer8/src/Extensions/HttpResponseExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Extensions; public static class HttpResponseExtensions { public static async Task WriteJsonAsync(this HttpResponse response, object o, string contentType = null) { var json = ObjectSerializer.ToString(o); await response.WriteJsonAsync(json, contentType); await response.Body.FlushAsync(); } public static async Task WriteJsonAsync(this HttpResponse response, string json, string contentType = null) { response.ContentType = contentType ?? "application/json; charset=UTF-8"; await response.WriteAsync(json); await response.Body.FlushAsync(); } public static void SetCache(this HttpResponse response, int maxAge, params string[] varyBy) { if (maxAge == 0) { SetNoCache(response); } else if (maxAge > 0) { if (!response.Headers.ContainsKey("Cache-Control")) { response.Headers.Append("Cache-Control", $"max-age={maxAge}"); } if (varyBy?.Any() == true) { var vary = varyBy.Aggregate((x, y) => x + "," + y); if (response.Headers.ContainsKey("Vary")) { vary = response.Headers["Vary"].ToString() + "," + vary; } response.Headers["Vary"] = vary; } } } public static void SetNoCache(this HttpResponse response) { if (!response.Headers.ContainsKey("Cache-Control")) { response.Headers.Append("Cache-Control", "no-store, no-cache, max-age=0"); } else { response.Headers["Cache-Control"] = "no-store, no-cache, max-age=0"; } if (!response.Headers.ContainsKey("Pragma")) { response.Headers.Append("Pragma", "no-cache"); } } public static async Task WriteHtmlAsync(this HttpResponse response, string html) { response.ContentType = "text/html; charset=UTF-8"; await response.WriteAsync(html, Encoding.UTF8); await response.Body.FlushAsync(); } public static void RedirectToAbsoluteUrl(this HttpResponse response, string url) { if (url.IsLocalUrl()) { if (url.StartsWith("~/")) url = url.Substring(1); url = response.HttpContext.GetIdentityServerBaseUrl().EnsureTrailingSlash() + url.RemoveLeadingSlash(); } response.RedirectIfAllowed(url); } public static void AddScriptCspHeaders(this HttpResponse response, CspOptions options, string hash) { var csp1part = options.Level == CspLevel.One ? "'unsafe-inline' " : string.Empty; var cspHeader = $"default-src 'none'; script-src {csp1part}'{hash}'"; AddCspHeaders(response.Headers, options, cspHeader); } public static void AddStyleCspHeaders(this HttpResponse response, CspOptions options, string hash, string frameSources) { var csp1part = options.Level == CspLevel.One ? "'unsafe-inline' " : string.Empty; var cspHeader = $"default-src 'none'; style-src {csp1part}'{hash}'"; if (!string.IsNullOrEmpty(frameSources)) { cspHeader += $"; frame-src {frameSources}"; } AddCspHeaders(response.Headers, options, cspHeader); } public static void AddCspHeaders(IHeaderDictionary headers, CspOptions options, string cspHeader) { if (!headers.ContainsKey("Content-Security-Policy")) { headers.Append("Content-Security-Policy", cspHeader); } if (options.AddDeprecatedHeader && !headers.ContainsKey("X-Content-Security-Policy")) { headers.Append("X-Content-Security-Policy", cspHeader); } } } ================================================ FILE: src/IdentityServer8/src/Extensions/ICacheExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; /// /// Extensions for ICache /// public static class ICacheExtensions { /// /// Attempts to get an item from the cache. If the item is not found, the get function is used to /// obtain the item and populate the cache. /// /// /// The cache. /// The key. /// The duration. /// The get function. /// The logger. /// /// cache /// or /// get /// cache /// or /// get public static async Task GetAsync(this ICache cache, string key, TimeSpan duration, Func> get, ILogger logger) where T : class { if (cache == null) throw new ArgumentNullException(nameof(cache)); if (get == null) throw new ArgumentNullException(nameof(get)); if (key == null) return null; var item = await cache.GetAsync(key); if (item == null) { logger.LogTrace("Cache miss for {cacheKey}", Ioc.Sanitizer.Log.Sanitize(key)); item = await get(); if (item != null) { logger.LogTrace("Setting item in cache for {cacheKey}", Ioc.Sanitizer.Log.Sanitize(key)); await cache.SetAsync(key, item, duration); } } else { logger.LogTrace("Cache hit for {cacheKey}", Ioc.Sanitizer.Log.Sanitize(key)); } return item; } } ================================================ FILE: src/IdentityServer8/src/Extensions/IClientStoreExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Extension for IClientStore /// public static class IClientStoreExtensions { /// /// Finds the enabled client by identifier. /// /// The store. /// The client identifier. /// public static async Task FindEnabledClientByIdAsync(this IClientStore store, string clientId) { var client = await store.FindClientByIdAsync(clientId); if (client != null && client.Enabled) return client; return null; } } ================================================ FILE: src/IdentityServer8/src/Extensions/IEnumerableExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Extensions; public static class IEnumerableExtensions { [DebuggerStepThrough] public static bool EnumerableIsNullOrEmpty(this IEnumerable list) { if (list == null) { return true; } if (!list.Any()) { return true; } return false; } public static bool HasDuplicates(this IEnumerable list, Func selector) { var d = new HashSet(); foreach (var t in list) { if (!d.Add(selector(t))) { return true; } } return false; } } ================================================ FILE: src/IdentityServer8/src/Extensions/IReadableStringCollectionExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Extensions; public static class IReadableStringCollectionExtensions { [DebuggerStepThrough] public static NameValueCollection AsNameValueCollection(this IEnumerable> collection) { var nv = new NameValueCollection(); foreach (var field in collection) { nv.Add(field.Key, field.Value.First()); } return nv; } [DebuggerStepThrough] public static NameValueCollection AsNameValueCollection(this IDictionary collection) { var nv = new NameValueCollection(); foreach (var field in collection) { nv.Add(field.Key, field.Value.First()); } return nv; } } ================================================ FILE: src/IdentityServer8/src/Extensions/IResourceStoreExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Extensions for IResourceStore /// public static class IResourceStoreExtensions { /// /// Finds the resources by scope. /// /// The store. /// The scope names. /// public static async Task FindResourcesByScopeAsync(this IResourceStore store, IEnumerable scopeNames) { var identity = await store.FindIdentityResourcesByScopeNameAsync(scopeNames); var apiResources = await store.FindApiResourcesByScopeNameAsync(scopeNames); var scopes = await store.FindApiScopesByNameAsync(scopeNames); Validate(identity, apiResources, scopes); var resources = new Resources(identity, apiResources, scopes) { OfflineAccess = scopeNames.Contains(IdentityServerConstants.StandardScopes.OfflineAccess) }; return resources; } private static void Validate(IEnumerable identity, IEnumerable apiResources, IEnumerable apiScopes) { // attempt to detect invalid configuration. this is about the only place // we can do this, since it's hard to get the values in the store. var identityScopeNames = identity.Select(x => x.Name).ToArray(); var dups = GetDuplicates(identityScopeNames); if (dups.Any()) { var names = dups.Aggregate((x, y) => x + ", " + y); throw new Exception( $"Duplicate identity scopes found. This is an invalid configuration. Use different names for identity scopes. Scopes found: {names}"); } var apiNames = apiResources.Select(x => x.Name); dups = GetDuplicates(apiNames); if (dups.Any()) { var names = dups.Aggregate((x, y) => x + ", " + y); throw new Exception( $"Duplicate api resources found. This is an invalid configuration. Use different names for API resources. Names found: {names}"); } var scopesNames = apiScopes.Select(x => x.Name); dups = GetDuplicates(scopesNames); if (dups.Any()) { var names = dups.Aggregate((x, y) => x + ", " + y); throw new Exception( $"Duplicate scopes found. This is an invalid configuration. Use different names for scopes. Names found: {names}"); } var overlap = identityScopeNames.Intersect(scopesNames).ToArray(); if (overlap.Any()) { var names = overlap.Aggregate((x, y) => x + ", " + y); throw new Exception( $"Found identity scopes and API scopes that use the same names. This is an invalid configuration. Use different names for identity scopes and API scopes. Scopes found: {names}"); } } private static IEnumerable GetDuplicates(IEnumerable names) { var duplicates = names .GroupBy(x => x) .Where(g => g.Count() > 1) .Select(y => y.Key) .ToArray(); return duplicates.ToArray(); } /// /// Finds the enabled resources by scope. /// /// The store. /// The scope names. /// public static async Task FindEnabledResourcesByScopeAsync(this IResourceStore store, IEnumerable scopeNames) { return (await store.FindResourcesByScopeAsync(scopeNames)).FilterEnabled(); } /// /// Creates a resource validation result. /// /// The store. /// The parsed scopes. /// public static async Task CreateResourceValidationResult(this IResourceStore store, ParsedScopesResult parsedScopesResult) { var validScopeValues = parsedScopesResult.ParsedScopes; var scopes = validScopeValues.Select(x => x.ParsedName).ToArray(); var resources = await store.FindEnabledResourcesByScopeAsync(scopes); return new ResourceValidationResult(resources, validScopeValues); } /// /// Gets all enabled resources. /// /// The store. /// public static async Task GetAllEnabledResourcesAsync(this IResourceStore store) { var resources = await store.GetAllResourcesAsync(); Validate(resources.IdentityResources, resources.ApiResources, resources.ApiScopes); return resources.FilterEnabled(); } /// /// Finds the enabled identity resources by scope. /// /// The store. /// The scope names. /// public static async Task> FindEnabledIdentityResourcesByScopeAsync(this IResourceStore store, IEnumerable scopeNames) { return (await store.FindIdentityResourcesByScopeNameAsync(scopeNames)).Where(x => x.Enabled).ToArray(); } } ================================================ FILE: src/IdentityServer8/src/Extensions/IUserSessionExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Extension for IUserSession. /// public static class IUserSessionExtensions { /// /// Creates a LogoutNotificationContext for the current user session. /// /// public static async Task GetLogoutNotificationContext(this IUserSession session) { var clientIds = await session.GetClientListAsync(); if (clientIds.Any()) { var user = await session.GetUserAsync(); var sub = user.GetSubjectId(); var sid = await session.GetSessionIdAsync(); return new LogoutNotificationContext { SubjectId = sub, SessionId = sid, ClientIds = clientIds }; } return null; } } ================================================ FILE: src/IdentityServer8/src/Extensions/IdentityServerToolsExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8; /// /// Extensions for IdentityServerTools /// public static class IdentityServerToolsExtensions { /// /// Issues the client JWT. /// /// The tools. /// The client identifier. /// The lifetime. /// The scopes. /// The audiences. /// Additional claims /// public static async Task IssueClientJwtAsync(this IdentityServerTools tools, string clientId, int lifetime, IEnumerable scopes = null, IEnumerable audiences = null, IEnumerable additionalClaims = null) { var claims = new HashSet(new ClaimComparer()); var context = tools.ContextAccessor.HttpContext; var options = context.RequestServices.GetRequiredService(); if (additionalClaims != null) { foreach (var claim in additionalClaims) { claims.Add(claim); } } claims.Add(new Claim(JwtClaimTypes.ClientId, clientId)); if (!scopes.EnumerableIsNullOrEmpty()) { foreach (var scope in scopes) { claims.Add(new Claim(JwtClaimTypes.Scope, scope)); } } if (options.EmitStaticAudienceClaim) { claims.Add(new Claim(JwtClaimTypes.Audience, string.Format(IdentityServerConstants.AccessTokenAudience, tools.ContextAccessor.HttpContext.GetIdentityServerIssuerUri().EnsureTrailingSlash()))); } if (!audiences.EnumerableIsNullOrEmpty()) { foreach (var audience in audiences) { claims.Add(new Claim(JwtClaimTypes.Audience, audience)); } } return await tools.IssueJwtAsync(lifetime, claims); } } ================================================ FILE: src/IdentityServer8/src/Extensions/JsonExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using JsonSerializer = System.Text.Json.JsonSerializer; namespace IdentityServer8.Extensions; internal static partial class JsonExtensions { [DebuggerStepThrough] public static T ToObject(this JsonElement element, JsonSerializerOptions options = null) { var bufferWriter = new ArrayBufferWriter(); using (var writer = new Utf8JsonWriter(bufferWriter)) element.WriteTo(writer); return JsonSerializer.Deserialize(bufferWriter.WrittenSpan, options); } [DebuggerStepThrough] public static T ToObject(this JsonDocument document, JsonSerializerOptions options = null) { if (document == null) throw new ArgumentNullException(nameof(document)); return document.RootElement.ToObject(options); } } ================================================ FILE: src/IdentityServer8/src/Extensions/NameValueCollectionExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; internal static class NameValueCollectionExtensions { public static IDictionary ToFullDictionary(this NameValueCollection source) { return source.AllKeys.ToDictionary(k => k, k => source.GetValues(k)); } public static NameValueCollection FromFullDictionary(this IDictionary source) { var nvc = new NameValueCollection(); foreach ((string key, string[] strings) in source) { foreach (var value in strings) { nvc.Add(key, value); } } return nvc; } public static string ToQueryString(this NameValueCollection collection) { if (collection.Count == 0) { return String.Empty; } var builder = new StringBuilder(128); var first = true; foreach (string name in collection) { var values = collection.GetValues(name); if (values == null || values.Length == 0) { first = AppendNameValuePair(builder, first, true, name, String.Empty); } else { foreach (var value in values) { first = AppendNameValuePair(builder, first, true, name, value); } } } return builder.ToString(); } public static string ToFormPost(this NameValueCollection collection) { var builder = new StringBuilder(128); const string inputFieldFormat = "\n"; foreach (string name in collection) { var values = collection.GetValues(name); var value = values.First(); value = HtmlEncoder.Default.Encode(value); builder.AppendFormat(inputFieldFormat, name, value); } return builder.ToString(); } public static NameValueCollection ToNameValueCollection(this Dictionary data) { var result = new NameValueCollection(); if (data == null || data.Count == 0) { return result; } foreach (var name in data.Keys) { var value = data[name]; if (value != null) { result.Add(name, value); } } return result; } public static Dictionary ToDictionary(this NameValueCollection collection) { return collection.ToScrubbedDictionary(); } public static Dictionary ToScrubbedDictionary(this NameValueCollection collection, params string[] nameFilter) { var dict = new Dictionary(); if (collection == null || collection.Count == 0) { return dict; } foreach (string name in collection) { var value = collection.Get(name); if (value != null) { if (nameFilter.Contains(name, StringComparer.OrdinalIgnoreCase)) { value = "***REDACTED***"; } dict.Add(name, value); } } return dict; } internal static string ConvertFormUrlEncodedSpacesToUrlEncodedSpaces(string str) { if ((str != null) && (str.IndexOf('+') >= 0)) { str = str.Replace("+", "%20"); } return str; } private static bool AppendNameValuePair(StringBuilder builder, bool first, bool urlEncode, string name, string value) { var effectiveName = name ?? String.Empty; var encodedName = urlEncode ? UrlEncoder.Default.Encode(effectiveName) : effectiveName; var effectiveValue = value ?? String.Empty; var encodedValue = urlEncode ? UrlEncoder.Default.Encode(effectiveValue) : effectiveValue; encodedValue = ConvertFormUrlEncodedSpacesToUrlEncodedSpaces(encodedValue); if (first) { first = false; } else { builder.Append("&"); } builder.Append(encodedName); if (!String.IsNullOrEmpty(encodedValue)) { builder.Append("="); builder.Append(encodedValue); } return first; } } ================================================ FILE: src/IdentityServer8/src/Extensions/PrincipalExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; /// /// Extension methods for and . /// public static class PrincipalExtensions { /// /// Gets the authentication time. /// /// The principal. /// [DebuggerStepThrough] public static DateTime GetAuthenticationTime(this IPrincipal principal) { return DateTimeOffset.FromUnixTimeSeconds(principal.GetAuthenticationTimeEpoch()).UtcDateTime; } /// /// Gets the authentication epoch time. /// /// The principal. /// [DebuggerStepThrough] public static long GetAuthenticationTimeEpoch(this IPrincipal principal) { return principal.Identity.GetAuthenticationTimeEpoch(); } /// /// Gets the authentication epoch time. /// /// The identity. /// [DebuggerStepThrough] public static long GetAuthenticationTimeEpoch(this IIdentity identity) { var id = identity as ClaimsIdentity; var claim = id.FindFirst(JwtClaimTypes.AuthenticationTime); if (claim == null) throw new InvalidOperationException("auth_time is missing."); return long.Parse(claim.Value); } /// /// Gets the subject identifier. /// /// The principal. /// [DebuggerStepThrough] public static string GetSubjectId(this IPrincipal principal) { return principal.Identity.GetSubjectId(); } /// /// Gets the subject identifier. /// /// The identity. /// /// sub claim is missing [DebuggerStepThrough] public static string GetSubjectId(this IIdentity identity) { var id = identity as ClaimsIdentity; var claim = id.FindFirst(JwtClaimTypes.Subject); if (claim == null) throw new InvalidOperationException("sub claim is missing"); return claim.Value; } /// /// Gets the name. /// /// The principal. /// [DebuggerStepThrough] [Obsolete("This method will be removed in a future version. Use GetDisplayName instead.")] public static string GetName(this IPrincipal principal) { return principal.Identity.GetName(); } /// /// Gets the name. /// /// The principal. /// [DebuggerStepThrough] public static string GetDisplayName(this ClaimsPrincipal principal) { var name = principal.Identity.Name; if (name.IsPresent()) return name; var sub = principal.FindFirst(JwtClaimTypes.Subject); if (sub != null) return sub.Value; return string.Empty; } /// /// Gets the name. /// /// The identity. /// /// name claim is missing [DebuggerStepThrough] [Obsolete("This method will be removed in a future version. Use GetDisplayName instead.")] public static string GetName(this IIdentity identity) { var id = identity as ClaimsIdentity; var claim = id.FindFirst(JwtClaimTypes.Name); if (claim == null) throw new InvalidOperationException("name claim is missing"); return claim.Value; } /// /// Gets the authentication method. /// /// The principal. /// [DebuggerStepThrough] public static string GetAuthenticationMethod(this IPrincipal principal) { return principal.Identity.GetAuthenticationMethod(); } /// /// Gets the authentication method claims. /// /// The principal. /// [DebuggerStepThrough] public static IEnumerable GetAuthenticationMethods(this IPrincipal principal) { return principal.Identity.GetAuthenticationMethods(); } /// /// Gets the authentication method. /// /// The identity. /// /// amr claim is missing [DebuggerStepThrough] public static string GetAuthenticationMethod(this IIdentity identity) { var id = identity as ClaimsIdentity; var claim = id.FindFirst(JwtClaimTypes.AuthenticationMethod); if (claim == null) throw new InvalidOperationException("amr claim is missing"); return claim.Value; } /// /// Gets the authentication method claims. /// /// The identity. /// [DebuggerStepThrough] public static IEnumerable GetAuthenticationMethods(this IIdentity identity) { var id = identity as ClaimsIdentity; return id.FindAll(JwtClaimTypes.AuthenticationMethod); } /// /// Gets the identity provider. /// /// The principal. /// [DebuggerStepThrough] public static string GetIdentityProvider(this IPrincipal principal) { return principal.Identity.GetIdentityProvider(); } /// /// Gets the identity provider. /// /// The identity. /// /// idp claim is missing [DebuggerStepThrough] public static string GetIdentityProvider(this IIdentity identity) { var id = identity as ClaimsIdentity; var claim = id.FindFirst(JwtClaimTypes.IdentityProvider); if (claim == null) throw new InvalidOperationException("idp claim is missing"); return claim.Value; } /// /// Determines whether this instance is authenticated. /// /// The principal. /// /// true if the specified principal is authenticated; otherwise, false. /// [DebuggerStepThrough] public static bool IsAuthenticated(this IPrincipal principal) { return principal != null && principal.Identity != null && principal.Identity.IsAuthenticated; } } ================================================ FILE: src/IdentityServer8/src/Extensions/ProfileDataRequestContextExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Extensions for ProfileDataRequestContext /// public static class ProfileDataRequestContextExtensions { /// /// Filters the claims based on requested claim types. /// /// The context. /// The claims. /// public static List FilterClaims(this ProfileDataRequestContext context, IEnumerable claims) { if (context == null) throw new ArgumentNullException(nameof(context)); if (claims == null) throw new ArgumentNullException(nameof(claims)); return claims.Where(x => context.RequestedClaimTypes.Contains(x.Type)).ToList(); } /// /// Filters the claims based on the requested claim types and then adds them to the IssuedClaims collection. /// /// The context. /// The claims. public static void AddRequestedClaims(this ProfileDataRequestContext context, IEnumerable claims) { if (context.RequestedClaimTypes.Any()) { context.IssuedClaims.AddRange(context.FilterClaims(claims)); } } /// /// Logs the profile request. /// /// The context. /// The logger. public static void LogProfileRequest(this ProfileDataRequestContext context, ILogger logger) { logger.LogDebug("Get profile called for subject {subject} from client {client} with claim types {claimTypes} via {caller}", context.Subject.GetSubjectId(), context.Client.ClientName ?? context.Client.ClientId, context.RequestedClaimTypes, context.Caller); } /// /// Logs the issued claims. /// /// The context. /// The logger. public static void LogIssuedClaims(this ProfileDataRequestContext context, ILogger logger) { logger.LogDebug("Issued claims: {claims}", context.IssuedClaims.Select(c => c.Type)); } } ================================================ FILE: src/IdentityServer8/src/Extensions/ResourceExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Extensions for Resource /// public static class ResourceExtensions { /// /// Returns the collection of scope values that are required. /// /// /// public static IEnumerable GetRequiredScopeValues(this ResourceValidationResult resourceValidationResult) { var names = resourceValidationResult.Resources.IdentityResources.Where(x => x.Required).Select(x => x.Name).ToList(); names.AddRange(resourceValidationResult.Resources.ApiScopes.Where(x => x.Required).Select(x => x.Name)); var values = resourceValidationResult.ParsedScopes.Where(x => names.Contains(x.ParsedName)).Select(x => x.RawValue); return values; } /// /// Converts to scope names. /// /// The resources. /// public static IEnumerable ToScopeNames(this Resources resources) { var names = resources.IdentityResources.Select(x => x.Name).ToList(); names.AddRange(resources.ApiScopes.Select(x => x.Name)); if (resources.OfflineAccess) { names.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } return names; } /// /// Finds the IdentityResource that matches the scope. /// /// The resources. /// The name. /// public static IdentityResource FindIdentityResourcesByScope(this Resources resources, string name) { var q = from id in resources.IdentityResources where id.Name == name select id; return q.FirstOrDefault(); } /// /// Finds the API resources that contain the scope. /// /// The resources. /// The name. /// public static IEnumerable FindApiResourcesByScope(this Resources resources, string name) { var q = from api in resources.ApiResources where api.Scopes != null && api.Scopes.Contains(name) select api; return q.ToArray(); } /// /// Finds the API scope. /// /// The resources. /// The name. /// public static ApiScope FindApiScope(this Resources resources, string name) { var q = from scope in resources.ApiScopes where scope.Name == name select scope; return q.FirstOrDefault(); } internal static Resources FilterEnabled(this Resources resources) { if (resources == null) return new Resources(); return new Resources( resources.IdentityResources.Where(x => x.Enabled), resources.ApiResources.Where(x => x.Enabled), resources.ApiScopes.Where(x => x.Enabled)) { OfflineAccess = resources.OfflineAccess }; } internal static ICollection FindMatchingSigningAlgorithms(this IEnumerable apiResources) { var apis = apiResources.ToList(); if (apis.EnumerableIsNullOrEmpty()) { return new List(); } // only one API resource request, forward the allowed signing algorithms (if any) if (apis.Count == 1) { return apis.First().AllowedAccessTokenSigningAlgorithms; } var allAlgorithms = apis.Where(r => r.AllowedAccessTokenSigningAlgorithms.Any()).Select(r => r.AllowedAccessTokenSigningAlgorithms).ToList(); // resources need to agree on allowed signing algorithms if (allAlgorithms.Any()) { var allowedAlgorithms = IntersectLists(allAlgorithms); if (allowedAlgorithms.Any()) { return allowedAlgorithms.ToHashSet(); } throw new InvalidOperationException("Signing algorithms requirements for requested resources are not compatible."); } return new List(); } private static IEnumerable IntersectLists(IEnumerable> lists) { return lists.Aggregate((l1, l2) => l1.Intersect(l2)); } } ================================================ FILE: src/IdentityServer8/src/Extensions/ScopeExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; internal static class ScopeExtensions { [DebuggerStepThrough] public static string ToSpaceSeparatedString(this IEnumerable apiScopes) { var scopeNames = from s in apiScopes select s.Name; return string.Join(" ", scopeNames.ToArray()); } [DebuggerStepThrough] public static IEnumerable ToStringList(this IEnumerable apiScopes) { var scopeNames = from s in apiScopes select s.Name; return scopeNames; } } ================================================ FILE: src/IdentityServer8/src/Extensions/StringsExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; internal static class StringExtensions { [DebuggerStepThrough] public static string ToSpaceSeparatedString(this IEnumerable list) { if (list == null) { return string.Empty; } var sb = new StringBuilder(100); foreach (var element in list) { sb.Append(element + " "); } return sb.ToString().Trim(); } [DebuggerStepThrough] public static IEnumerable FromSpaceSeparatedString(this string input) { input = input.Trim(); return input.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).ToList(); } public static List ParseScopesString(this string scopes) { if (scopes.IsMissing()) { return null; } scopes = scopes.Trim(); var parsedScopes = scopes.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries).Distinct().ToList(); if (parsedScopes.Any()) { parsedScopes.Sort(); return parsedScopes; } return null; } [DebuggerStepThrough] public static bool IsMissing(this string value) { return string.IsNullOrWhiteSpace(value); } [DebuggerStepThrough] public static bool IsMissingOrTooLong(this string value, int maxLength) { if (string.IsNullOrWhiteSpace(value)) { return true; } if (value.Length > maxLength) { return true; } return false; } [DebuggerStepThrough] public static bool IsPresent(this string value) { return !string.IsNullOrWhiteSpace(value); } [DebuggerStepThrough] public static string EnsureLeadingSlash(this string url) { if (url != null && !url.StartsWith("/")) { return "/" + url; } return url; } [DebuggerStepThrough] public static string EnsureTrailingSlash(this string url) { if (url != null && !url.EndsWith("/")) { return url + "/"; } return url; } [DebuggerStepThrough] public static string RemoveLeadingSlash(this string url) { if (url != null && url.StartsWith("/")) { url = url.Substring(1); } return url; } [DebuggerStepThrough] public static string RemoveTrailingSlash(this string url) { if (url != null && url.EndsWith("/")) { url = url.Substring(0, url.Length - 1); } return url; } [DebuggerStepThrough] public static string CleanUrlPath(this string url) { if (String.IsNullOrWhiteSpace(url)) url = "/"; if (url != "/" && url.EndsWith("/")) { url = url.Substring(0, url.Length - 1); } return url; } [DebuggerStepThrough] public static bool IsLocalUrl(this string url) { if (string.IsNullOrEmpty(url)) { return false; } // Allows "/" or "/foo" but not "//" or "/\". if (url[0] == '/') { // url is exactly "/" if (url.Length == 1) { return true; } // url doesn't start with "//" or "/\" if (url[1] != '/' && url[1] != '\\') { return true; } return false; } // Allows "~/" or "~/foo" but not "~//" or "~/\". if (url[0] == '~' && url.Length > 1 && url[1] == '/') { // url is exactly "~/" if (url.Length == 2) { return true; } // url doesn't start with "~//" or "~/\" if (url[2] != '/' && url[2] != '\\') { return true; } return false; } return false; } [DebuggerStepThrough] public static string AddQueryString(this string url, string query) { if (!url.Contains("?")) { url += "?"; } else if (!url.EndsWith("&")) { url += "&"; } return url + query; } [DebuggerStepThrough] public static string AddQueryString(this string url, string name, string value) { return url.AddQueryString(name + "=" + UrlEncoder.Default.Encode(value)); } [DebuggerStepThrough] public static string AddHashFragment(this string url, string query) { if (!url.Contains("#")) { url += "#"; } return url + query; } [DebuggerStepThrough] public static NameValueCollection ReadQueryStringAsNameValueCollection(this string url) { if (url != null) { var idx = url.IndexOf('?'); if (idx >= 0) { url = url.Substring(idx + 1); } var query = QueryHelpers.ParseNullableQuery(url); if (query != null) { return query.AsNameValueCollection(); } } return new NameValueCollection(); } public static string GetOrigin(this string url) { if (url != null) { Uri uri; try { uri = new Uri(url); } catch (Exception) { return null; } if (uri.Scheme == "http" || uri.Scheme == "https") { return $"{uri.Scheme}://{uri.Authority}"; } } return null; } public static string Obfuscate(this string value) { var last4Chars = "****"; if (value.IsPresent() && value.Length > 4) { last4Chars = value.Substring(value.Length - 4); } return "****" + last4Chars; } } ================================================ FILE: src/IdentityServer8/src/Extensions/TokenExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; /// /// Extensions for Token /// public static class TokenExtensions { /// /// Creates the default JWT payload. /// /// The token. /// The clock. /// The options /// The logger. /// /// /// public static JwtPayload CreateJwtPayload(this Token token, ISystemClock clock, IdentityServerOptions options, ILogger logger) { var payload = new JwtPayload( token.Issuer, null, null, clock.UtcNow.UtcDateTime, clock.UtcNow.UtcDateTime.AddSeconds(token.Lifetime)); foreach (var aud in token.Audiences) { payload.AddClaim(new Claim(JwtClaimTypes.Audience, aud)); } var amrClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.AuthenticationMethod).ToArray(); var scopeClaims = token.Claims.Where(x => x.Type == JwtClaimTypes.Scope).ToArray(); var jsonClaims = token.Claims.Where(x => x.ValueType == IdentityServerConstants.ClaimValueTypes.Json).ToList(); // add confirmation claim if present (it's JSON valued) if (token.Confirmation.IsPresent()) { jsonClaims.Add(new Claim(JwtClaimTypes.Confirmation, token.Confirmation, IdentityServerConstants.ClaimValueTypes.Json)); } var normalClaims = token.Claims .Except(amrClaims) .Except(jsonClaims) .Except(scopeClaims); payload.AddClaims(normalClaims); // scope claims if (!scopeClaims.EnumerableIsNullOrEmpty()) { var scopeValues = scopeClaims.Select(x => x.Value).ToArray(); if (options.EmitScopesAsSpaceDelimitedStringInJwt) { payload.Add(JwtClaimTypes.Scope, string.Join(" ", scopeValues)); } else { payload.Add(JwtClaimTypes.Scope, scopeValues); } } // amr claims if (!amrClaims.EnumerableIsNullOrEmpty()) { var amrValues = amrClaims.Select(x => x.Value).Distinct().ToArray(); payload.Add(JwtClaimTypes.AuthenticationMethod, amrValues); } // deal with json types // calling ToArray() to trigger JSON parsing once and so later // collection identity comparisons work for the anonymous type try { var jsonTokens = jsonClaims.Select(x => new { x.Type, JsonValue = JRaw.Parse(x.Value) }).ToArray(); var jsonObjects = jsonTokens.Where(x => x.JsonValue.Type == JTokenType.Object).ToArray(); var jsonObjectGroups = jsonObjects.GroupBy(x => x.Type).ToArray(); foreach (var group in jsonObjectGroups) { if (payload.ContainsKey(group.Key)) { throw new Exception($"Can't add two claims where one is a JSON object and the other is not a JSON object ({group.Key})"); } if (group.Skip(1).Any()) { // add as array payload.Add(group.Key, group.Select(x => x.JsonValue).ToArray()); } else { // add just one payload.Add(group.Key, group.First().JsonValue); } } var jsonArrays = jsonTokens.Where(x => x.JsonValue.Type == JTokenType.Array).ToArray(); var jsonArrayGroups = jsonArrays.GroupBy(x => x.Type).ToArray(); foreach (var group in jsonArrayGroups) { if (payload.ContainsKey(group.Key)) { throw new Exception( $"Can't add two claims where one is a JSON array and the other is not a JSON array ({group.Key})"); } var newArr = new List(); foreach (var arrays in group) { var arr = (JArray)arrays.JsonValue; newArr.AddRange(arr); } // add just one array for the group/key/claim type payload.Add(group.Key, newArr.ToArray()); } var unsupportedJsonTokens = jsonTokens.Except(jsonObjects).Except(jsonArrays).ToArray(); var unsupportedJsonClaimTypes = unsupportedJsonTokens.Select(x => x.Type).Distinct().ToArray(); if (unsupportedJsonClaimTypes.Any()) { throw new Exception( $"Unsupported JSON type for claim types: {unsupportedJsonClaimTypes.Aggregate((x, y) => x + ", " + y)}"); } return payload; } catch (Exception ex) { logger.LogCritical(ex, "Error creating a JSON valued claim"); throw; } } } ================================================ FILE: src/IdentityServer8/src/Extensions/ValidatedAuthorizeRequestExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Validation; public static class ValidatedAuthorizeRequestExtensions { public static void RemovePrompt(this ValidatedAuthorizeRequest request) { request.PromptModes = Enumerable.Empty(); request.Raw.Remove(OidcConstants.AuthorizeRequest.Prompt); } public static string GetPrefixedAcrValue(this ValidatedAuthorizeRequest request, string prefix) { var value = request.AuthenticationContextReferenceClasses .FirstOrDefault(x => x.StartsWith(prefix)); if (value != null) { value = value.Substring(prefix.Length); } return value; } public static void RemovePrefixedAcrValue(this ValidatedAuthorizeRequest request, string prefix) { request.AuthenticationContextReferenceClasses.RemoveAll(acr => acr.StartsWith(prefix, StringComparison.Ordinal)); var acr_values = request.AuthenticationContextReferenceClasses.ToSpaceSeparatedString(); if (acr_values.IsPresent()) { request.Raw[OidcConstants.AuthorizeRequest.AcrValues] = acr_values; } else { request.Raw.Remove(OidcConstants.AuthorizeRequest.AcrValues); } } public static string GetIdP(this ValidatedAuthorizeRequest request) { return request.GetPrefixedAcrValue(Constants.KnownAcrValues.HomeRealm); } public static void RemoveIdP(this ValidatedAuthorizeRequest request) { request.RemovePrefixedAcrValue(Constants.KnownAcrValues.HomeRealm); } public static string GetTenant(this ValidatedAuthorizeRequest request) { return request.GetPrefixedAcrValue(Constants.KnownAcrValues.Tenant); } public static IEnumerable GetAcrValues(this ValidatedAuthorizeRequest request) { return request .AuthenticationContextReferenceClasses .Where(acr => !Constants.KnownAcrValues.All.Any(well_known => acr.StartsWith(well_known))) .Distinct() .ToArray(); } public static void RemoveAcrValue(this ValidatedAuthorizeRequest request, string value) { request.AuthenticationContextReferenceClasses.RemoveAll(x => x.Equals(value, StringComparison.Ordinal)); var acr_values = request.AuthenticationContextReferenceClasses.ToSpaceSeparatedString(); if (acr_values.IsPresent()) { request.Raw[OidcConstants.AuthorizeRequest.AcrValues] = acr_values; } else { request.Raw.Remove(OidcConstants.AuthorizeRequest.AcrValues); } } public static void AddAcrValue(this ValidatedAuthorizeRequest request, string value) { if (String.IsNullOrWhiteSpace(value)) throw new ArgumentNullException(nameof(value)); request.AuthenticationContextReferenceClasses.Add(value); var acr_values = request.AuthenticationContextReferenceClasses.ToSpaceSeparatedString(); request.Raw[OidcConstants.AuthorizeRequest.AcrValues] = acr_values; } public static string GenerateSessionStateValue(this ValidatedAuthorizeRequest request) { if (request == null) return null; if (!request.IsOpenIdRequest) return null; if (request.SessionId == null) return null; if (request.ClientId.IsMissing()) return null; if (request.RedirectUri.IsMissing()) return null; var clientId = request.ClientId; var sessionId = request.SessionId; var salt = CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex); var uri = new Uri(request.RedirectUri); var origin = uri.Scheme + "://" + uri.Host; if (!uri.IsDefaultPort) { origin += ":" + uri.Port; } var bytes = Encoding.UTF8.GetBytes(clientId + origin + sessionId + salt); byte[] hash; using (var sha = SHA256.Create()) { hash = sha.ComputeHash(bytes); } return Base64Url.Encode(hash) + "." + salt; } } ================================================ FILE: src/IdentityServer8/src/Extensions/X509CertificateExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using JsonSerializer = System.Text.Json.JsonSerializer; namespace IdentityServer8.Extensions; /// /// Extensions methods for X509Certificate2 /// public static class X509CertificateExtensions { /// /// Create the value of a thumbprint-based cnf claim /// /// /// public static string CreateThumbprintCnf(this X509Certificate2 certificate) { var hash = certificate.GetCertHash(HashAlgorithmName.SHA256); var values = new Dictionary { { "x5t#S256", Base64Url.Encode(hash) } }; return JsonSerializer.Serialize(values); } } ================================================ FILE: src/IdentityServer8/src/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8; global using IdentityServer8.Configuration; global using IdentityServer8.Configuration.DependencyInjection; global using IdentityServer8.Endpoints; global using IdentityServer8.Endpoints.Results; global using IdentityServer8.Events; global using IdentityServer8.Extensions; global using IdentityServer8.Hosting; global using IdentityServer8.Hosting.FederatedSignOut; global using IdentityServer8.Hosting.LocalApiAuthentication; global using IdentityServer8.Infrastructure; global using IdentityServer8.Logging; global using IdentityServer8.Logging.Models; global using IdentityServer8.Models; global using IdentityServer8.ResponseHandling; global using IdentityServer8.Services; global using IdentityServer8.Services.Default; global using IdentityServer8.Stores; global using IdentityServer8.Stores.Serialization; global using IdentityServer8.Test; global using IdentityServer8.Validation; global using Microsoft.AspNetCore.Authentication; global using Microsoft.AspNetCore.Authentication.Cookies; global using Microsoft.AspNetCore.Authentication.OpenIdConnect; global using Microsoft.AspNetCore.Builder; global using Microsoft.AspNetCore.Cors.Infrastructure; //global using Microsoft.AspNetCore.DataProtection; global using Microsoft.AspNetCore.Http; global using Microsoft.AspNetCore.WebUtilities; global using Microsoft.Extensions.Caching.Distributed; global using Microsoft.Extensions.Caching.Memory; global using Microsoft.Extensions.Configuration; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.DependencyInjection.Extensions; global using Microsoft.Extensions.Logging; global using Microsoft.Extensions.Options; global using Microsoft.Extensions.Primitives; //global using Microsoft.IdentityModel.Tokens; //global using Microsoft.Net.Http.Headers; global using Newtonsoft.Json; global using Newtonsoft.Json.Linq; global using System.Buffers; global using System.Collections.Concurrent; global using System.Collections.Specialized; global using System.Diagnostics; global using System.Globalization; global using System.IdentityModel.Tokens.Jwt; global using System.Net; global using System.Reflection; global using System.Runtime.CompilerServices; global using System.Security.Claims; global using System.Security.Cryptography; global using System.Security.Cryptography.X509Certificates; global using System.Security.Principal; global using System.Text; global using System.Text.Encodings.Web; global using System.Text.Json; global using System.Text.Json.Serialization; global using static IdentityServer8.Constants; global using static IdentityServer8.IdentityServerConstants; global using ClaimValueTypes = System.Security.Claims.ClaimValueTypes; ================================================ FILE: src/IdentityServer8/src/Hosting/BaseUrlMiddleware.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Hosting; public class BaseUrlMiddleware { private readonly RequestDelegate _next; private readonly IdentityServerOptions _options; public BaseUrlMiddleware(RequestDelegate next, IdentityServerOptions options) { _next = next; _options = options; } public async Task Invoke(HttpContext context) { var request = context.Request; context.SetIdentityServerBasePath(request.PathBase.Value.RemoveTrailingSlash()); await _next(context); } } ================================================ FILE: src/IdentityServer8/src/Hosting/CorsMiddleware.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Hosting; public static class CorsMiddlewareExtensions { public static void ConfigureCors(this IApplicationBuilder app) { var options = app.ApplicationServices.GetRequiredService(); app.UseCors(options.Cors.CorsPolicyName); } } ================================================ FILE: src/IdentityServer8/src/Hosting/CorsPolicyProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting; internal class CorsPolicyProvider : ICorsPolicyProvider { private readonly ILogger _logger; private readonly ICorsPolicyProvider _inner; private readonly IdentityServerOptions _options; private readonly IHttpContextAccessor _httpContext; public CorsPolicyProvider( ILogger logger, Decorator inner, IdentityServerOptions options, IHttpContextAccessor httpContext) { _logger = logger; _inner = inner.Instance; _options = options; _httpContext = httpContext; } public Task GetPolicyAsync(HttpContext context, string policyName) { if (_options.Cors.CorsPolicyName == policyName) { return ProcessAsync(context); } else { return _inner.GetPolicyAsync(context, policyName); } } private async Task ProcessAsync(HttpContext context) { var origin = context.Request.GetCorsOrigin(); if (origin != null) { var path = context.Request.Path; if (IsPathAllowed(path)) { var sanitizedOrigin = origin.SanitizeForLog(); _logger.LogDebug("CORS request made for path: {path} from origin: {origin}", path.SanitizeForLog(), sanitizedOrigin); // manually resolving this from DI because this: // https://github.com/aspnet/CORS/issues/105 var corsPolicyService = _httpContext.HttpContext.RequestServices.GetRequiredService(); if (await corsPolicyService.IsOriginAllowedAsync(origin)) { _logger.LogDebug("CorsPolicyService allowed origin: {origin}", sanitizedOrigin); return Allow(origin); } else { _logger.LogWarning("CorsPolicyService did not allow origin: {origin}", sanitizedOrigin); } } else { _logger.LogDebug("CORS request made for path: {path} from origin: {origin} but was ignored because path was not for an allowed IdentityServer CORS endpoint", Ioc.Sanitizer.Log.Sanitize(path), Ioc.Sanitizer.Log.Sanitize(origin)); } } return null; } private CorsPolicy Allow(string origin) { var policyBuilder = new CorsPolicyBuilder() .WithOrigins(origin) .AllowAnyHeader() .AllowAnyMethod(); if (_options.Cors.PreflightCacheDuration.HasValue) { policyBuilder.SetPreflightMaxAge(_options.Cors.PreflightCacheDuration.Value); } return policyBuilder.Build(); } private bool IsPathAllowed(PathString path) { return _options.Cors.CorsPaths.Any(x => path == x); } } ================================================ FILE: src/IdentityServer8/src/Hosting/Endpoint.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Hosting; public class Endpoint { public Endpoint(string name, string path, Type handlerType) { Name = name; Path = path; Handler = handlerType; } /// /// Gets or sets the name. /// /// /// The name. /// public string Name { get; set; } /// /// Gets or sets the path. /// /// /// The path. /// public PathString Path { get; set; } /// /// Gets or sets the handler. /// /// /// The handler. /// public Type Handler { get; set; } } ================================================ FILE: src/IdentityServer8/src/Hosting/EndpointRouter.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting; internal class EndpointRouter : IEndpointRouter { private readonly IEnumerable _endpoints; private readonly IdentityServerOptions _options; private readonly ILogger _logger; public EndpointRouter(IEnumerable endpoints, IdentityServerOptions options, ILogger logger) { _endpoints = endpoints; _options = options; _logger = logger; } public IEndpointHandler Find(HttpContext context) { if (context == null) throw new ArgumentNullException(nameof(context)); foreach(var endpoint in _endpoints) { var path = endpoint.Path; if (context.Request.Path.Equals(path, StringComparison.OrdinalIgnoreCase)) { var endpointName = endpoint.Name; _logger.LogDebug("Request path {path} matched to endpoint type {endpoint}", Ioc.Sanitizer.Log.Sanitize(context.Request.Path), endpointName); return GetEndpointHandler(endpoint, context); } } _logger.LogTrace("No endpoint entry found for request path: {path}", Ioc.Sanitizer.Log.Sanitize(context.Request.Path)); return null; } private IEndpointHandler GetEndpointHandler(Endpoint endpoint, HttpContext context) { if (_options.Endpoints.IsEndpointEnabled(endpoint)) { if (context.RequestServices.GetService(endpoint.Handler) is IEndpointHandler handler) { _logger.LogDebug("Endpoint enabled: {endpoint}, successfully created handler: {endpointHandler}", endpoint.Name, endpoint.Handler.FullName); return handler; } _logger.LogDebug("Endpoint enabled: {endpoint}, failed to create handler: {endpointHandler}", endpoint.Name, endpoint.Handler.FullName); } else { _logger.LogWarning("Endpoint disabled: {endpoint}", endpoint.Name); } return null; } } ================================================ FILE: src/IdentityServer8/src/Hosting/FederatedSignOut/AuthenticationRequestHandlerWrapper.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting.FederatedSignOut; internal class AuthenticationRequestHandlerWrapper : IAuthenticationRequestHandler { private const string IframeHtml = ""; private readonly IAuthenticationRequestHandler _inner; private readonly HttpContext _context; private readonly ILogger _logger; public AuthenticationRequestHandlerWrapper(IAuthenticationRequestHandler inner, IHttpContextAccessor httpContextAccessor) { _inner = inner; _context = httpContextAccessor.HttpContext; var factory = (ILoggerFactory)_context.RequestServices.GetService(typeof(ILoggerFactory)); _logger = factory?.CreateLogger(GetType()); } public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) { return _inner.InitializeAsync(scheme, context); } public async Task HandleRequestAsync() { var result = await _inner.HandleRequestAsync(); if (result && _context.GetSignOutCalled() && _context.Response.StatusCode == 200) { // given that this runs prior to the authentication middleware running // we need to explicitly trigger authentication so we can have our // session service populated with the current user info await _context.AuthenticateAsync(); // now we can do our processing to render the iframe (if needed) await ProcessFederatedSignOutRequestAsync(); } return result; } public Task AuthenticateAsync() { return _inner.AuthenticateAsync(); } public Task ChallengeAsync(AuthenticationProperties properties) { return _inner.ChallengeAsync(properties); } public Task ForbidAsync(AuthenticationProperties properties) { return _inner.ForbidAsync(properties); } private async Task ProcessFederatedSignOutRequestAsync() { _logger?.LogDebug("Processing federated signout"); var iframeUrl = await _context.GetIdentityServerSignoutFrameCallbackUrlAsync(); if (iframeUrl != null) { _logger?.LogDebug("Rendering signout callback iframe"); await RenderResponseAsync(iframeUrl); } else { _logger?.LogDebug("No signout callback iframe to render"); } } private async Task RenderResponseAsync(string iframeUrl) { _context.Response.SetNoCache(); if (_context.Response.Body.CanWrite) { var iframe = String.Format(IframeHtml, iframeUrl); _context.Response.ContentType = "text/html"; await _context.Response.WriteAsync(iframe); await _context.Response.Body.FlushAsync(); } } } ================================================ FILE: src/IdentityServer8/src/Hosting/FederatedSignOut/AuthenticationRequestSignInHandlerWrapper.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting.FederatedSignOut; internal class AuthenticationRequestSignInHandlerWrapper : AuthenticationRequestSignOutHandlerWrapper, IAuthenticationSignInHandler { private readonly IAuthenticationSignInHandler _inner; public AuthenticationRequestSignInHandlerWrapper(IAuthenticationSignInHandler inner, IHttpContextAccessor httpContextAccessor) : base(inner, httpContextAccessor) { _inner = inner; } public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { return _inner.SignInAsync(user, properties); } } ================================================ FILE: src/IdentityServer8/src/Hosting/FederatedSignOut/AuthenticationRequestSignOutHandlerWrapper.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting.FederatedSignOut; internal class AuthenticationRequestSignOutHandlerWrapper : AuthenticationRequestHandlerWrapper, IAuthenticationSignOutHandler { private readonly IAuthenticationSignOutHandler _inner; public AuthenticationRequestSignOutHandlerWrapper(IAuthenticationSignOutHandler inner, IHttpContextAccessor httpContextAccessor) : base((IAuthenticationRequestHandler)inner, httpContextAccessor) { _inner = inner; } public Task SignOutAsync(AuthenticationProperties properties) { return _inner.SignOutAsync(properties); } } ================================================ FILE: src/IdentityServer8/src/Hosting/FederatedSignOut/FederatedSignoutAuthenticationHandlerProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting.FederatedSignOut; // this intercepts IAuthenticationRequestHandler authentication handlers // to detect when they are handling federated signout. when they are invoked, // call signout on the default authentication scheme, and return 200 then // we assume they are handling the federated signout in an iframe. // based on this assumption, we then render our federated signout iframes // to any current clients. internal class FederatedSignoutAuthenticationHandlerProvider : IAuthenticationHandlerProvider { private readonly IAuthenticationHandlerProvider _provider; private readonly IHttpContextAccessor _httpContextAccessor; public FederatedSignoutAuthenticationHandlerProvider( Decorator decorator, IHttpContextAccessor httpContextAccessor) { _provider = decorator.Instance; _httpContextAccessor = httpContextAccessor; } public async Task GetHandlerAsync(HttpContext context, string authenticationScheme) { var handler = await _provider.GetHandlerAsync(context, authenticationScheme); if (handler is IAuthenticationRequestHandler requestHandler) { if (requestHandler is IAuthenticationSignInHandler signinHandler) { return new AuthenticationRequestSignInHandlerWrapper(signinHandler, _httpContextAccessor); } if (requestHandler is IAuthenticationSignOutHandler signoutHandler) { return new AuthenticationRequestSignOutHandlerWrapper(signoutHandler, _httpContextAccessor); } return new AuthenticationRequestHandlerWrapper(requestHandler, _httpContextAccessor); } return handler; } } ================================================ FILE: src/IdentityServer8/src/Hosting/IEndpointHandler.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting; /// /// Endpoint handler /// public interface IEndpointHandler { /// /// Processes the request. /// /// The HTTP context. /// Task ProcessAsync(HttpContext context); } ================================================ FILE: src/IdentityServer8/src/Hosting/IEndpointResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting; /// /// Endpoint result /// public interface IEndpointResult { /// /// Executes the result. /// /// The HTTP context. /// Task ExecuteAsync(HttpContext context); } ================================================ FILE: src/IdentityServer8/src/Hosting/IEndpointRouter.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting; /// /// The endpoint router /// public interface IEndpointRouter { /// /// Finds a matching endpoint. /// /// The HTTP context. /// IEndpointHandler Find(HttpContext context); } ================================================ FILE: src/IdentityServer8/src/Hosting/IdentityServerAuthenticationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting; // this decorates the real authentication service to detect when the // user is being signed in. this allows us to ensure the user has // the claims needed for identity server to do its job. it also allows // us to track signin/signout so we can issue/remove the session id // cookie used for check session iframe for session management spec. // finally, we track if signout is called to collaborate with the // FederatedSignoutAuthenticationHandlerProvider for federated signout. internal class IdentityServerAuthenticationService : IAuthenticationService { private readonly IAuthenticationService _inner; private readonly IAuthenticationSchemeProvider _schemes; private readonly ISystemClock _clock; private readonly IUserSession _session; private readonly IBackChannelLogoutService _backChannelLogoutService; private readonly IdentityServerOptions _options; private readonly ILogger _logger; public IdentityServerAuthenticationService( Decorator decorator, IAuthenticationSchemeProvider schemes, ISystemClock clock, IUserSession session, IBackChannelLogoutService backChannelLogoutService, IdentityServerOptions options, ILogger logger) { _inner = decorator.Instance; _schemes = schemes; _clock = clock; _session = session; _backChannelLogoutService = backChannelLogoutService; _options = options; _logger = logger; } public async Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) { var defaultScheme = await _schemes.GetDefaultSignInSchemeAsync(); var cookieScheme = await context.GetCookieAuthenticationSchemeAsync(); if ((scheme == null && defaultScheme?.Name == cookieScheme) || scheme == cookieScheme) { AugmentPrincipal(principal); properties ??= new AuthenticationProperties(); await _session.CreateSessionIdAsync(principal, properties); } await _inner.SignInAsync(context, scheme, principal, properties); } private void AugmentPrincipal(ClaimsPrincipal principal) { _logger.LogDebug("Augmenting SignInContext"); AssertRequiredClaims(principal); AugmentMissingClaims(principal, _clock.UtcNow.UtcDateTime); } public async Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties) { var defaultScheme = await _schemes.GetDefaultSignOutSchemeAsync(); var cookieScheme = await context.GetCookieAuthenticationSchemeAsync(); if ((scheme == null && defaultScheme?.Name == cookieScheme) || scheme == cookieScheme) { // this sets a flag used by middleware to do post-signout work. context.SetSignOutCalled(); } await _inner.SignOutAsync(context, scheme, properties); } public Task AuthenticateAsync(HttpContext context, string scheme) { return _inner.AuthenticateAsync(context, scheme); } public Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties) { return _inner.ChallengeAsync(context, scheme, properties); } public Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties) { return _inner.ForbidAsync(context, scheme, properties); } private void AssertRequiredClaims(ClaimsPrincipal principal) { // for now, we don't allow more than one identity in the principal/cookie if (principal.Identities.Count() != 1) throw new InvalidOperationException("only a single identity supported"); if (principal.FindFirst(JwtClaimTypes.Subject) == null) throw new InvalidOperationException("sub claim is missing"); } private void AugmentMissingClaims(ClaimsPrincipal principal, DateTime authTime) { var identity = principal.Identities.First(); // ASP.NET Identity issues this claim type and uses the authentication middleware name // such as "Google" for the value. this code is trying to correct/convert that for // our scenario. IOW, we take their old AuthenticationMethod value of "Google" // and issue it as the idp claim. we then also issue a amr with "external" var amr = identity.FindFirst(ClaimTypes.AuthenticationMethod); if (amr != null && identity.FindFirst(JwtClaimTypes.IdentityProvider) == null && identity.FindFirst(JwtClaimTypes.AuthenticationMethod) == null) { _logger.LogDebug("Removing amr claim with value: {value}", amr.Value); identity.RemoveClaim(amr); _logger.LogDebug("Adding idp claim with value: {value}", amr.Value); identity.AddClaim(new Claim(JwtClaimTypes.IdentityProvider, amr.Value)); _logger.LogDebug("Adding amr claim with value: {value}", Constants.ExternalAuthenticationMethod); identity.AddClaim(new Claim(JwtClaimTypes.AuthenticationMethod, Constants.ExternalAuthenticationMethod)); } if (identity.FindFirst(JwtClaimTypes.IdentityProvider) == null) { _logger.LogDebug("Adding idp claim with value: {value}", IdentityServerConstants.LocalIdentityProvider); identity.AddClaim(new Claim(JwtClaimTypes.IdentityProvider, IdentityServerConstants.LocalIdentityProvider)); } if (identity.FindFirst(JwtClaimTypes.AuthenticationMethod) == null) { if (identity.FindFirst(JwtClaimTypes.IdentityProvider).Value == IdentityServerConstants.LocalIdentityProvider) { _logger.LogDebug("Adding amr claim with value: {value}", OidcConstants.AuthenticationMethods.Password); identity.AddClaim(new Claim(JwtClaimTypes.AuthenticationMethod, OidcConstants.AuthenticationMethods.Password)); } else { _logger.LogDebug("Adding amr claim with value: {value}", Constants.ExternalAuthenticationMethod); identity.AddClaim(new Claim(JwtClaimTypes.AuthenticationMethod, Constants.ExternalAuthenticationMethod)); } } if (identity.FindFirst(JwtClaimTypes.AuthenticationTime) == null) { var time = new DateTimeOffset(authTime).ToUnixTimeSeconds().ToString(); _logger.LogDebug("Adding auth_time claim with value: {value}", time); identity.AddClaim(new Claim(JwtClaimTypes.AuthenticationTime, time, ClaimValueTypes.Integer64)); } } } ================================================ FILE: src/IdentityServer8/src/Hosting/IdentityServerMiddleware.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting; /// /// IdentityServer middleware /// public class IdentityServerMiddleware { private readonly RequestDelegate _next; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The next. /// The logger. public IdentityServerMiddleware(RequestDelegate next, ILogger logger) { _next = next; _logger = logger; } /// /// Invokes the middleware. /// /// The context. /// The router. /// The user session. /// The event service. /// /// public async Task Invoke(HttpContext context, IEndpointRouter router, IUserSession session, IEventService events, IBackChannelLogoutService backChannelLogoutService) { // this will check the authentication session and from it emit the check session // cookie needed from JS-based signout clients. await session.EnsureSessionIdCookieAsync(); context.Response.OnStarting(async () => { if (context.GetSignOutCalled()) { _logger.LogDebug("SignOutCalled set; processing post-signout session cleanup."); // this clears our session id cookie so JS clients can detect the user has signed out await session.RemoveSessionIdCookieAsync(); // back channel logout var logoutContext = await session.GetLogoutNotificationContext(); if (logoutContext != null) { await backChannelLogoutService.SendLogoutNotificationsAsync(logoutContext); } } }); try { var endpoint = router.Find(context); if (endpoint != null) { _logger.LogInformation("Invoking IdentityServer endpoint: {endpointType} for {url}", endpoint.GetType().FullName, context.Request.Path.ToString()); var result = await endpoint.ProcessAsync(context); if (result != null) { _logger.LogTrace("Invoking result: {type}", result.GetType().FullName); await result.ExecuteAsync(context); } return; } } catch (Exception ex) { await events.RaiseAsync(new UnhandledExceptionEvent(ex)); _logger.LogCritical(ex, "Unhandled exception: {exception}", ex.Message); throw; } await _next(context); } } ================================================ FILE: src/IdentityServer8/src/Hosting/LocalApiAuthentication/LocalApiAuthenticationEvents.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting.LocalApiAuthentication; /// /// Events for local API authentication /// public class LocalApiAuthenticationEvents { /// /// Invoked after the security token has passed validation and a ClaimsIdentity has been generated. /// public Func OnClaimsTransformation { get; set; } = context => Task.CompletedTask; /// /// Invoked after the security token has passed validation and a ClaimsIdentity has been generated. /// public virtual Task ClaimsTransformation(ClaimsTransformationContext context) => OnClaimsTransformation(context); } /// /// Context class for local API claims transformation /// public class ClaimsTransformationContext { /// /// The principal /// public ClaimsPrincipal Principal { get; set; } /// /// the HTTP context /// public HttpContext HttpContext { get; internal set; } } ================================================ FILE: src/IdentityServer8/src/Hosting/LocalApiAuthentication/LocalApiAuthenticationExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// Extensions for registering the local access token authentication handler /// public static class LocalApiAuthenticationExtensions { /// /// Adds support for local APIs /// /// The service collection /// Function to transform the resulting principal /// public static IServiceCollection AddLocalApiAuthentication(this IServiceCollection services, Func> transformationFunc = null) { services.AddAuthentication() .AddLocalApi(options => { options.ExpectedScope = IdentityServerConstants.LocalApi.ScopeName; if (transformationFunc != null) { options.Events = new LocalApiAuthenticationEvents { OnClaimsTransformation = async e => { e.Principal = await transformationFunc(e.Principal); } }; } }); services.AddAuthorization(options => { options.AddPolicy(IdentityServerConstants.LocalApi.PolicyName, policy => { policy.AddAuthenticationSchemes(IdentityServerConstants.LocalApi.AuthenticationScheme); policy.RequireAuthenticatedUser(); }); }); return services; } /// /// Registers the authentication handler for local APIs. /// /// The builder. /// public static AuthenticationBuilder AddLocalApi(this AuthenticationBuilder builder) => builder.AddLocalApi(IdentityServerConstants.LocalApi.AuthenticationScheme, _ => { }); /// /// Registers the authentication handler for local APIs. /// /// The builder. /// The configure options. /// public static AuthenticationBuilder AddLocalApi(this AuthenticationBuilder builder, Action configureOptions) => builder.AddLocalApi(IdentityServerConstants.LocalApi.AuthenticationScheme, configureOptions); /// /// Registers the authentication handler for local APIs. /// /// The builder. /// The authentication scheme. /// The configure options. /// public static AuthenticationBuilder AddLocalApi(this AuthenticationBuilder builder, string authenticationScheme, Action configureOptions) => builder.AddLocalApi(authenticationScheme, displayName: null, configureOptions: configureOptions); /// /// Registers the authentication handler for local APIs. /// /// The builder. /// The authentication scheme. /// The display name of this scheme. /// The configure options. /// public static AuthenticationBuilder AddLocalApi(this AuthenticationBuilder builder, string authenticationScheme, string displayName, Action configureOptions) { return builder.AddScheme(authenticationScheme, displayName, configureOptions); } } ================================================ FILE: src/IdentityServer8/src/Hosting/LocalApiAuthentication/LocalApiAuthenticationHandler.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting.LocalApiAuthentication; /// /// Authentication handler for validating access token from the local IdentityServer /// public class LocalApiAuthenticationHandler : AuthenticationHandler { private readonly ITokenValidator _tokenValidator; private readonly ILogger _logger; /// public LocalApiAuthenticationHandler(IOptionsMonitor options, ILoggerFactory logger, UrlEncoder encoder, ISystemClock clock, ITokenValidator tokenValidator) : base(options, logger, encoder, clock) { _tokenValidator = tokenValidator; _logger = logger.CreateLogger(); } /// /// The handler calls methods on the events which give the application control at certain points where processing is occurring. /// If it is not provided a default instance is supplied which does nothing when the methods are called. /// protected new LocalApiAuthenticationEvents Events { get => (LocalApiAuthenticationEvents)base.Events; set => base.Events = value; } /// protected override Task CreateEventsAsync() => Task.FromResult(new LocalApiAuthenticationEvents()); /// protected override async Task HandleAuthenticateAsync() { _logger.LogTrace("HandleAuthenticateAsync called"); string token = null; string authorization = Request.Headers["Authorization"]; if (string.IsNullOrEmpty(authorization)) { return AuthenticateResult.NoResult(); } if (authorization.StartsWith("Bearer ", StringComparison.OrdinalIgnoreCase)) { token = authorization.Substring("Bearer ".Length).Trim(); } if (string.IsNullOrEmpty(token)) { return AuthenticateResult.Fail("No Access Token is sent."); } _logger.LogTrace("Token found: {token}", Ioc.Sanitizer.Log.Sanitize(token)); TokenValidationResult result = await _tokenValidator.ValidateAccessTokenAsync(token, Options.ExpectedScope); if (result.IsError) { _logger.LogTrace("Failed to validate the token"); return AuthenticateResult.Fail(result.Error); } _logger.LogTrace("Successfully validated the token."); ClaimsIdentity claimsIdentity = new ClaimsIdentity(result.Claims, Scheme.Name, JwtClaimTypes.Name, JwtClaimTypes.Role); ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity); AuthenticationProperties authenticationProperties = new AuthenticationProperties(); if (Options.SaveToken) { authenticationProperties.StoreTokens(new[] { new AuthenticationToken { Name = "access_token", Value = token } }); } var claimsTransformationContext = new ClaimsTransformationContext { Principal = claimsPrincipal, HttpContext = Context }; await Events.ClaimsTransformation(claimsTransformationContext); AuthenticationTicket authenticationTicket = new AuthenticationTicket(claimsTransformationContext.Principal, authenticationProperties, Scheme.Name); return AuthenticateResult.Success(authenticationTicket); } } ================================================ FILE: src/IdentityServer8/src/Hosting/LocalApiAuthentication/LocalApiAuthenticationOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting.LocalApiAuthentication; /// /// Options for local API authentication /// /// public class LocalApiAuthenticationOptions : AuthenticationSchemeOptions { /// /// Allows setting a specific required scope (optional) /// public string ExpectedScope { get; set; } /// /// Specifies whether the token should be saved in the authentication properties /// public bool SaveToken { get; set; } = true; /// /// Allows implementing events /// public new LocalApiAuthenticationEvents Events { get { return (LocalApiAuthenticationEvents)base.Events; } set { base.Events = value; } } } ================================================ FILE: src/IdentityServer8/src/Hosting/MutualTlsEndpointMiddleware.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Hosting; /// /// Middleware for re-writing the MTLS enabled endpoints to the standard protocol endpoints /// public class MutualTlsEndpointMiddleware { private readonly ILogger _logger; private readonly RequestDelegate _next; private readonly IdentityServerOptions _options; /// /// ctor /// /// /// /// public MutualTlsEndpointMiddleware(RequestDelegate next, IdentityServerOptions options, ILogger logger) { _next = next; _options = options; _logger = logger; } /// public async Task Invoke(HttpContext context, IAuthenticationSchemeProvider schemes) { if (_options.MutualTls.Enabled) { // domain-based MTLS if (_options.MutualTls.DomainName.IsPresent()) { // separate domain if (_options.MutualTls.DomainName.Contains(".")) { if (context.Request.Host.Host.Equals(_options.MutualTls.DomainName, StringComparison.OrdinalIgnoreCase)) { var result = await TriggerCertificateAuthentication(context); if (!result.Succeeded) { return; } } } // sub-domain else { if (context.Request.Host.Host.StartsWith(_options.MutualTls.DomainName + ".", StringComparison.OrdinalIgnoreCase)) { var result = await TriggerCertificateAuthentication(context); if (!result.Succeeded) { return; } } } } // path based MTLS else if (context.Request.Path.StartsWithSegments(Constants.ProtocolRoutePaths.MtlsPathPrefix.EnsureLeadingSlash(), out var subPath)) { var result = await TriggerCertificateAuthentication(context); if (result.Succeeded) { var path = Constants.ProtocolRoutePaths.ConnectPathPrefix + subPath.ToString().EnsureLeadingSlash(); path = path.EnsureLeadingSlash(); _logger.LogDebug("Rewriting MTLS request from: {oldPath} to: {newPath}", context.Request.Path.ToString(), path); context.Request.Path = path; } else { return; } } } await _next(context); } private async Task TriggerCertificateAuthentication(HttpContext context) { var x509AuthResult = await context.AuthenticateAsync(_options.MutualTls.ClientCertificateAuthenticationScheme); if (!x509AuthResult.Succeeded) { _logger.LogDebug("MTLS authentication failed, error: {error}.", x509AuthResult.Failure?.Message); await context.ForbidAsync(_options.MutualTls.ClientCertificateAuthenticationScheme); } return x509AuthResult; } } ================================================ FILE: src/IdentityServer8/src/IdentityServer8.csproj ================================================ true true True True ================================================ FILE: src/IdentityServer8/src/IdentityServerConstants.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 using Microsoft.IdentityModel.Tokens; namespace IdentityServer8; public static class IdentityServerConstants { public const string LocalIdentityProvider = "local"; public const string DefaultCookieAuthenticationScheme = "idsrv"; public const string SignoutScheme = "idsrv"; public const string ExternalCookieAuthenticationScheme = "idsrv.external"; public const string DefaultCheckSessionCookieName = "idsrv.session"; public const string AccessTokenAudience = "{0}resources"; public const string JwtRequestClientKey = "idsrv.jwtrequesturi.client"; /// /// Constants for local IdentityServer access token authentication. /// public static class LocalApi { /// /// The authentication scheme when using the AddLocalApi helper. /// public const string AuthenticationScheme = "IdentityServerAccessToken"; /// /// The API scope name when using the AddLocalApiAuthentication helper. /// public const string ScopeName = "IdentityServerApi"; /// /// The authorization policy name when using the AddLocalApiAuthentication helper. /// public const string PolicyName = AuthenticationScheme; } public static class ProtocolTypes { public const string OpenIdConnect = "oidc"; public const string WsFederation = "wsfed"; public const string Saml2p = "saml2p"; } public static class TokenTypes { public const string IdentityToken = "id_token"; public const string AccessToken = "access_token"; } public static class ClaimValueTypes { public const string Json = "json"; } public static class ParsedSecretTypes { public const string NoSecret = "NoSecret"; public const string SharedSecret = "SharedSecret"; public const string X509Certificate = "X509Certificate"; public const string JwtBearer = "urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; } public static class SecretTypes { public const string SharedSecret = "SharedSecret"; public const string X509CertificateThumbprint = "X509Thumbprint"; public const string X509CertificateName = "X509Name"; public const string X509CertificateBase64 = "X509CertificateBase64"; public const string JsonWebKey = "JWK"; } public static class ProfileDataCallers { public const string UserInfoEndpoint = "UserInfoEndpoint"; public const string ClaimsProviderIdentityToken = "ClaimsProviderIdentityToken"; public const string ClaimsProviderAccessToken = "ClaimsProviderAccessToken"; } public static class ProfileIsActiveCallers { public const string AuthorizeEndpoint = "AuthorizeEndpoint"; public const string IdentityTokenValidation = "IdentityTokenValidation"; public const string AccessTokenValidation = "AccessTokenValidation"; public const string ResourceOwnerValidation = "ResourceOwnerValidation"; public const string ExtensionGrantValidation = "ExtensionGrantValidation"; public const string RefreshTokenValidation = "RefreshTokenValidation"; public const string AuthorizationCodeValidation = "AuthorizationCodeValidation"; public const string UserInfoRequestValidation = "UserInfoRequestValidation"; public const string DeviceCodeValidation = "DeviceCodeValidation"; } public static IEnumerable SupportedSigningAlgorithms = new List { SecurityAlgorithms.RsaSha256, SecurityAlgorithms.RsaSha384, SecurityAlgorithms.RsaSha512, SecurityAlgorithms.RsaSsaPssSha256, SecurityAlgorithms.RsaSsaPssSha384, SecurityAlgorithms.RsaSsaPssSha512, SecurityAlgorithms.EcdsaSha256, SecurityAlgorithms.EcdsaSha384, SecurityAlgorithms.EcdsaSha512 }; public enum RsaSigningAlgorithm { RS256, RS384, RS512, PS256, PS384, PS512 } public enum ECDsaSigningAlgorithm { ES256, ES384, ES512 } public static class StandardScopes { /// REQUIRED. Informs the Authorization Server that the Client is making an OpenID Connect request. If the openid scope value is not present, the behavior is entirely unspecified. public const string OpenId = "openid"; /// OPTIONAL. This scope value requests access to the End-User's default profile Claims, which are: name, family_name, given_name, middle_name, nickname, preferred_username, profile, picture, website, gender, birthdate, zoneinfo, locale, and updated_at. public const string Profile = "profile"; /// OPTIONAL. This scope value requests access to the email and email_verified Claims. public const string Email = "email"; /// OPTIONAL. This scope value requests access to the address Claim. public const string Address = "address"; /// OPTIONAL. This scope value requests access to the phone_number and phone_number_verified Claims. public const string Phone = "phone"; /// This scope value MUST NOT be used with the OpenID Connect Implicit Client Implementer's Guide 1.0. See the OpenID Connect Basic Client Implementer's Guide 1.0 (http://openid.net/specs/openid-connect-implicit-1_0.html#OpenID.Basic) for its usage in that subset of OpenID Connect. public const string OfflineAccess = "offline_access"; } public static class PersistedGrantTypes { public const string AuthorizationCode = "authorization_code"; public const string ReferenceToken = "reference_token"; public const string RefreshToken = "refresh_token"; public const string UserConsent = "user_consent"; public const string DeviceCode = "device_code"; public const string UserCode = "user_code"; } public static class UserCodeTypes { public const string Numeric = "Numeric"; } public static class HttpClients { public const int DefaultTimeoutSeconds = 10; public const string JwtRequestUriHttpClient = "IdentityServer:JwtRequestUriClient"; public const string BackChannelLogoutHttpClient = "IdentityServer:BackChannelLogoutClient"; } } ================================================ FILE: src/IdentityServer8/src/IdentityServerTools.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8; /// /// Class for useful helpers for interacting with IdentityServer /// public class IdentityServerTools { internal readonly IHttpContextAccessor ContextAccessor; private readonly ITokenCreationService _tokenCreation; private readonly ISystemClock _clock; /// /// Initializes a new instance of the class. /// /// The context accessor. /// The token creation service. /// The clock. public IdentityServerTools(IHttpContextAccessor contextAccessor, ITokenCreationService tokenCreation, ISystemClock clock) { ContextAccessor = contextAccessor; _tokenCreation = tokenCreation; _clock = clock; } /// /// Issues a JWT. /// /// The lifetime. /// The claims. /// /// claims public virtual async Task IssueJwtAsync(int lifetime, IEnumerable claims) { if (claims == null) throw new ArgumentNullException(nameof(claims)); var issuer = ContextAccessor.HttpContext.GetIdentityServerIssuerUri(); var token = new Token { CreationTime = _clock.UtcNow.UtcDateTime, Issuer = issuer, Lifetime = lifetime, Claims = new HashSet(claims, new ClaimComparer()) }; return await _tokenCreation.CreateTokenAsync(token); } /// /// Issues a JWT. /// /// The lifetime. /// The issuer. /// The claims. /// /// claims public virtual async Task IssueJwtAsync(int lifetime, string issuer, IEnumerable claims) { if (String.IsNullOrWhiteSpace(issuer)) throw new ArgumentNullException(nameof(issuer)); if (claims == null) throw new ArgumentNullException(nameof(claims)); var token = new Token { CreationTime = _clock.UtcNow.UtcDateTime, Issuer = issuer, Lifetime = lifetime, Claims = new HashSet(claims, new ClaimComparer()) }; return await _tokenCreation.CreateTokenAsync(token); } } ================================================ FILE: src/IdentityServer8/src/IdentityServerUser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8; /// /// Model properties of an IdentityServer user /// public class IdentityServerUser { /// /// Subject ID (mandatory) /// public string SubjectId { get; } /// /// Display name (optional) /// public string DisplayName { get; set; } /// /// Identity provider (optional) /// public string IdentityProvider { get; set; } /// /// Authentication methods /// public ICollection AuthenticationMethods { get; set; } = new HashSet(); /// /// Authentication time /// public DateTime? AuthenticationTime { get; set; } /// /// Additional claims /// public ICollection AdditionalClaims { get; set; } = new HashSet(new ClaimComparer()); /// /// Initializes a user identity /// /// The subject ID public IdentityServerUser(string subjectId) { if (subjectId.IsMissing()) throw new ArgumentException("SubjectId is mandatory", nameof(subjectId)); SubjectId = subjectId; } /// /// Creates an IdentityServer claims principal /// /// /// public ClaimsPrincipal CreatePrincipal() { if (SubjectId.IsMissing()) throw new ArgumentException("SubjectId is mandatory", nameof(SubjectId)); var claims = new List { new Claim(JwtClaimTypes.Subject, SubjectId) }; if (DisplayName.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.Name, DisplayName)); } if (IdentityProvider.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.IdentityProvider, IdentityProvider)); } if (AuthenticationTime.HasValue) { claims.Add(new Claim(JwtClaimTypes.AuthenticationTime, new DateTimeOffset(AuthenticationTime.Value).ToUnixTimeSeconds().ToString())); } if (AuthenticationMethods.Any()) { foreach (var amr in AuthenticationMethods) { claims.Add(new Claim(JwtClaimTypes.AuthenticationMethod, amr)); } } claims.AddRange(AdditionalClaims); var id = new ClaimsIdentity(claims.Distinct(new ClaimComparer()), Constants.IdentityServerAuthenticationType, JwtClaimTypes.Name, JwtClaimTypes.Role); return new ClaimsPrincipal(id); } } ================================================ FILE: src/IdentityServer8/src/Infrastructure/DistributedCacheStateDataFormatter.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.DataProtection; namespace IdentityServer8.Infrastructure; /// /// State formatter using IDistributedCache /// public class DistributedCacheStateDataFormatter : ISecureDataFormat { private readonly IHttpContextAccessor _httpContext; private readonly string _name; /// /// Initializes a new instance of the class. /// /// The HTTP context accessor. /// The scheme name. public DistributedCacheStateDataFormatter(IHttpContextAccessor httpContext, string name) { _httpContext = httpContext; _name = name; } private string CacheKeyPrefix => "DistributedCacheStateDataFormatter"; private IDistributedCache Cache => _httpContext.HttpContext.RequestServices.GetRequiredService(); private IDataProtector Protector => _httpContext.HttpContext.RequestServices.GetRequiredService().CreateProtector(CacheKeyPrefix, _name); /// /// Protects the specified data. /// /// The data. /// public string Protect(AuthenticationProperties data) { return Protect(data, null); } /// /// Protects the specified data. /// /// The data. /// The purpose. /// public string Protect(AuthenticationProperties data, string purpose) { var key = Guid.NewGuid().ToString(); var cacheKey = $"{CacheKeyPrefix}-{_name}-{purpose}-{key}"; var json = ObjectSerializer.ToString(data.Items); var options = new DistributedCacheEntryOptions(); if (data.ExpiresUtc.HasValue) { options.SetAbsoluteExpiration(data.ExpiresUtc.Value); } else { options.SetSlidingExpiration(Constants.DefaultCacheDuration); } // Rather than encrypt the full AuthenticationProperties // cache the data and encrypt the key that points to the data Cache.SetString(cacheKey, json, options); return Protector.Protect(key); } /// /// Unprotects the specified protected text. /// /// The protected text. /// public AuthenticationProperties Unprotect(string protectedText) { return Unprotect(protectedText, null); } /// /// Unprotects the specified protected text. /// /// The protected text. /// The purpose. /// public AuthenticationProperties Unprotect(string protectedText, string purpose) { if (String.IsNullOrWhiteSpace(protectedText)) { return null; } // Decrypt the key and retrieve the data from the cache. var key = Protector.Unprotect(protectedText); var cacheKey = $"{CacheKeyPrefix}-{_name}-{purpose}-{key}"; var json = Cache.GetString(cacheKey); if (json == null) { return null; } var items = ObjectSerializer.FromString>(json); var props = new AuthenticationProperties(items); return props; } } ================================================ FILE: src/IdentityServer8/src/Infrastructure/MessageCookie.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.DataProtection; namespace IdentityServer8; internal class MessageCookie { private readonly ILogger _logger; private readonly IdentityServerOptions _options; private readonly IHttpContextAccessor _context; private readonly IDataProtector _protector; public MessageCookie( ILogger> logger, IdentityServerOptions options, IHttpContextAccessor context, IDataProtectionProvider provider) { _logger = logger; _options = options; _context = context; _protector = provider.CreateProtector(MessageType); } private string MessageType => typeof(TModel).Name; private string Protect(Message message) { var json = ObjectSerializer.ToString(message); _logger.LogTrace("Protecting message: {0}", json); return _protector.Protect(json); } private Message Unprotect(string data) { var json = _protector.Unprotect(data); var message = ObjectSerializer.FromString>(json); return message; } private string CookiePrefix => MessageType + "."; private string GetCookieFullName(string id) { return CookiePrefix + id; } private string CookiePath => _context.HttpContext.GetIdentityServerBasePath().CleanUrlPath(); private IEnumerable GetCookieNames() { var key = CookiePrefix; foreach ((string name, var _) in _context.HttpContext.Request.Cookies) { if (name.StartsWith(key)) { yield return name; } } } private bool Secure => _context.HttpContext.Request.IsHttps; public void Write(string id, Message message) { ClearOverflow(); if (message == null) throw new ArgumentNullException(nameof(message)); var name = GetCookieFullName(id); var data = Protect(message); _context.HttpContext.Response.Cookies.Append( name, data, new CookieOptions { HttpOnly = true, Secure = Secure, Path = CookiePath, IsEssential = true // don't need to set same-site since cookie is expected to be sent // to only another page in this host. }); } public Message Read(string id) { if (id.IsMissing()) return null; var name = GetCookieFullName(id); return ReadByCookieName(name); } private Message ReadByCookieName(string name) { var data = _context.HttpContext.Request.Cookies[name]; if (data.IsPresent()) { try { return Unprotect(data); } catch(Exception ex) { _logger.LogError(ex, "Error unprotecting message cookie"); ClearByCookieName(name); } } return null; } protected internal void Clear(string id) { var name = GetCookieFullName(id); ClearByCookieName(name); } private void ClearByCookieName(string name) { _context.HttpContext.Response.Cookies.Append( name, ".", new CookieOptions { Expires = new DateTime(2000, 1, 1), HttpOnly = true, Secure = Secure, Path = CookiePath, IsEssential = true }); } private long GetCookieRank(string name) { // empty and invalid cookies are considered to be the oldest: var rank = DateTime.MinValue.Ticks; try { var message = ReadByCookieName(name); if (message != null) { // valid cookies are ranked based on their creation time: rank = message.Created; } } catch (CryptographicException e) { // cookie was protected with a different key/algorithm _logger.LogDebug(e, "Unable to unprotect cookie {0}", name); } return rank; } private void ClearOverflow() { var names = GetCookieNames(); var toKeep = _options.UserInteraction.CookieMessageThreshold; if (names.Count() >= toKeep) { var rankedCookieNames = from name in names let rank = GetCookieRank(name) orderby rank descending select name; var purge = rankedCookieNames.Skip(Math.Max(0, toKeep - 1)); foreach (var name in purge) { _logger.LogTrace("Purging stale cookie: {cookieName}", name); ClearByCookieName(name); } } } } ================================================ FILE: src/IdentityServer8/src/Infrastructure/ObjectSerializer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using JsonSerializer = System.Text.Json.JsonSerializer; namespace IdentityServer8; internal static class ObjectSerializer { private static readonly JsonSerializerOptions Options = new JsonSerializerOptions { IgnoreNullValues = true }; public static string ToString(object o) { return JsonSerializer.Serialize(o, Options); } public static T FromString(string value) { return JsonSerializer.Deserialize(value, Options); } } ================================================ FILE: src/IdentityServer8/src/Logging/LogSerializer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using JsonSerializer = System.Text.Json.JsonSerializer; namespace IdentityServer8.Logging; /// /// Helper to JSON serialize object data for logging. /// internal static class LogSerializer { static readonly JsonSerializerOptions Options = new JsonSerializerOptions { IgnoreNullValues = true, WriteIndented = true }; static LogSerializer() { Options.Converters.Add(new JsonStringEnumConverter()); } /// /// Serializes the specified object. /// /// The object. /// public static string Serialize(object logObject) { return JsonSerializer.Serialize(logObject, Options); } } ================================================ FILE: src/IdentityServer8/src/Logging/Models/AuthorizeRequestValidationLog.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Logging.Models; internal class AuthorizeRequestValidationLog { public string ClientId { get; set; } public string ClientName { get; set; } public string RedirectUri { get; set; } public IEnumerable AllowedRedirectUris { get; set; } public string SubjectId { get; set; } public string ResponseType { get; set; } public string ResponseMode { get; set; } public string GrantType { get; set; } public string RequestedScopes { get; set; } public string State { get; set; } public string UiLocales { get; set; } public string Nonce { get; set; } public IEnumerable AuthenticationContextReferenceClasses { get; set; } public string DisplayMode { get; set; } public string PromptMode { get; set; } public int? MaxAge { get; set; } public string LoginHint { get; set; } public string SessionId { get; set; } public Dictionary Raw { get; set; } public AuthorizeRequestValidationLog(ValidatedAuthorizeRequest request, IEnumerable sensitiveValuesFilter) { Raw = request.Raw.ToScrubbedDictionary(sensitiveValuesFilter.ToArray()); if (request.Client != null) { ClientId = request.Client.ClientId; ClientName = request.Client.ClientName; AllowedRedirectUris = request.Client.RedirectUris; } if (request.Subject != null) { var subjectClaim = request.Subject.FindFirst(JwtClaimTypes.Subject); if (subjectClaim != null) { SubjectId = subjectClaim.Value; } else { SubjectId = "anonymous"; } } if (request.AuthenticationContextReferenceClasses.Any()) { AuthenticationContextReferenceClasses = request.AuthenticationContextReferenceClasses; } RedirectUri = request.RedirectUri; ResponseType = request.ResponseType; ResponseMode = request.ResponseMode; GrantType = request.GrantType; RequestedScopes = request.RequestedScopes.ToSpaceSeparatedString(); State = request.State; UiLocales = request.UiLocales; Nonce = request.Nonce; DisplayMode = request.DisplayMode; PromptMode = request.PromptModes.ToSpaceSeparatedString(); LoginHint = request.LoginHint; MaxAge = request.MaxAge; SessionId = request.SessionId; } public override string ToString() { return LogSerializer.Serialize(this); } } ================================================ FILE: src/IdentityServer8/src/Logging/Models/AuthorizeResponseLog.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Logging.Models; internal class AuthorizeResponseLog { public string SubjectId { get; set; } public string ClientId { get; set; } public string RedirectUri { get; set; } public string State { get; set; } public string Scope { get; set; } public string Error { get; set; } public string ErrorDescription { get; set; } public AuthorizeResponseLog(AuthorizeResponse response) { ClientId = response.Request?.Client?.ClientId; SubjectId = response.Request?.Subject?.GetSubjectId(); RedirectUri = response.RedirectUri; State = response.State; Scope = response.Scope; Error = response.Error; ErrorDescription = response.ErrorDescription; } public override string ToString() { return LogSerializer.Serialize(this); } } ================================================ FILE: src/IdentityServer8/src/Logging/Models/DeviceAuthorizationRequestValidationLog.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Logging; internal class DeviceAuthorizationRequestValidationLog { public string ClientId { get; set; } public string ClientName { get; set; } public string Scopes { get; set; } public Dictionary Raw { get; set; } private static readonly string[] SensitiveValuesFilter = { OidcConstants.TokenRequest.ClientSecret, OidcConstants.TokenRequest.ClientAssertion }; public DeviceAuthorizationRequestValidationLog(ValidatedDeviceAuthorizationRequest request) { Raw = request.Raw.ToScrubbedDictionary(SensitiveValuesFilter); if (request.Client != null) { ClientId = request.Client.ClientId; ClientName = request.Client.ClientName; } if (request.RequestedScopes != null) { Scopes = request.RequestedScopes.ToSpaceSeparatedString(); } } public override string ToString() { return LogSerializer.Serialize(this); } } ================================================ FILE: src/IdentityServer8/src/Logging/Models/EndSessionRequestValidationLog.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Logging.Models; internal class EndSessionRequestValidationLog { public string ClientId { get; set; } public string ClientName { get; set; } public string SubjectId { get; set; } public string PostLogOutUri { get; set; } public string State { get; set; } public Dictionary Raw { get; set; } public EndSessionRequestValidationLog(ValidatedEndSessionRequest request) { Raw = request.Raw.ToScrubbedDictionary(OidcConstants.EndSessionRequest.IdTokenHint); SubjectId = "unknown"; var subjectClaim = request.Subject?.FindFirst(JwtClaimTypes.Subject); if (subjectClaim != null) { SubjectId = subjectClaim.Value; } if (request.Client != null) { ClientId = request.Client.ClientId; ClientName = request.Client.ClientName; } PostLogOutUri = request.PostLogOutUri; State = request.State; } public override string ToString() { return LogSerializer.Serialize(this); } } ================================================ FILE: src/IdentityServer8/src/Logging/Models/TokenRequestValidationLog.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Logging.Models; internal class TokenRequestValidationLog { public string ClientId { get; set; } public string ClientName { get; set; } public string GrantType { get; set; } public string Scopes { get; set; } public string AuthorizationCode { get; set; } public string RefreshToken { get; set; } public string UserName { get; set; } public IEnumerable AuthenticationContextReferenceClasses { get; set; } public string Tenant { get; set; } public string IdP { get; set; } public Dictionary Raw { get; set; } public TokenRequestValidationLog(ValidatedTokenRequest request, IEnumerable sensitiveValuesFilter) { Raw = request.Raw.ToScrubbedDictionary(sensitiveValuesFilter.ToArray()); if (request.Client != null) { ClientId = request.Client.ClientId; ClientName = request.Client.ClientName; } if (request.RequestedScopes != null) { Scopes = request.RequestedScopes.ToSpaceSeparatedString(); } GrantType = request.GrantType; AuthorizationCode = request.AuthorizationCodeHandle.Obfuscate(); RefreshToken = request.RefreshTokenHandle.Obfuscate(); UserName = request.UserName; } public override string ToString() { return LogSerializer.Serialize(this); } } ================================================ FILE: src/IdentityServer8/src/Logging/Models/TokenValidationLog.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Logging.Models; internal class TokenValidationLog { // identity token public string ClientId { get; set; } public string ClientName { get; set; } public bool ValidateLifetime { get; set; } // access token public string AccessTokenType { get; set; } public string ExpectedScope { get; set; } public string TokenHandle { get; set; } public string JwtId { get; set; } // both public Dictionary Claims { get; set; } public override string ToString() { return LogSerializer.Serialize(this); } } ================================================ FILE: src/IdentityServer8/src/Models/Contexts/IsActiveContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Context describing the is-active check /// public class IsActiveContext { /// /// Initializes a new instance of the class. /// public IsActiveContext(ClaimsPrincipal subject, Client client, string caller) { if (subject == null) throw new ArgumentNullException(nameof(subject)); if (client == null) throw new ArgumentNullException(nameof(client)); if (caller.IsMissing()) throw new ArgumentNullException(nameof(caller)); Subject = subject; Client = client; Caller = caller; IsActive = true; } /// /// Gets or sets the subject. /// /// /// The subject. /// public ClaimsPrincipal Subject { get; set; } /// /// Gets or sets the client. /// /// /// The client. /// public Client Client { get; set; } /// /// Gets or sets the caller. /// /// /// The caller. /// public string Caller { get; set; } /// /// Gets or sets a value indicating whether the subject is active and can recieve tokens. /// /// /// true if the subject is active; otherwise, false. /// public bool IsActive { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/Contexts/LogoutNotificationContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Provides the context necessary to construct a logout notificaiton. /// public class LogoutNotificationContext { /// /// The SubjectId of the user. /// public string SubjectId { get; set; } /// /// The session Id of the user's authentication session. /// public string SessionId { get; set; } /// /// The list of client Ids that the user has authenticated to. /// public IEnumerable ClientIds { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/Contexts/ProfileDataRequestContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Class describing the profile data request /// public class ProfileDataRequestContext { /// /// Initializes a new instance of the class. /// public ProfileDataRequestContext() { } /// /// Initializes a new instance of the class. /// /// The subject. /// The client. /// The caller. /// The requested claim types. public ProfileDataRequestContext(ClaimsPrincipal subject, Client client, string caller, IEnumerable requestedClaimTypes) { Subject = subject ?? throw new ArgumentNullException(nameof(subject)); Client = client ?? throw new ArgumentNullException(nameof(client)); Caller = caller ?? throw new ArgumentNullException(nameof(caller)); RequestedClaimTypes = requestedClaimTypes ?? throw new ArgumentNullException(nameof(requestedClaimTypes)); } /// /// Gets or sets the validatedRequest. /// /// /// The validatedRequest. /// public ValidatedRequest ValidatedRequest { get; set; } /// /// Gets or sets the subject. /// /// /// The subject. /// public ClaimsPrincipal Subject { get; set; } /// /// Gets or sets the requested claim types. /// /// /// The requested claim types. /// public IEnumerable RequestedClaimTypes { get; set; } /// /// Gets or sets the client id. /// /// /// The client id. /// public Client Client { get; set; } /// /// Gets or sets the caller. /// /// /// The caller. /// public string Caller { get; set; } /// /// Gets or sets the requested resources (if available). /// /// /// The resources. /// public ResourceValidationResult RequestedResources { get; set; } /// /// Gets or sets the issued claims. /// /// /// The issued claims. /// public List IssuedClaims { get; set; } = new List(); } ================================================ FILE: src/IdentityServer8/src/Models/DeviceFlowAuthorizationRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Represents contextual information about a device flow authorization request. /// public class DeviceFlowAuthorizationRequest { /// /// Gets or sets the client. /// /// /// The client. /// public Client Client { get; set; } /// /// Gets or sets the validated resources. /// /// /// The scopes requested. /// public ResourceValidationResult ValidatedResources { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/DeviceFlowInteractionResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Request object for device flow interaction /// public class DeviceFlowInteractionResult { /// /// Gets or sets the error description. /// /// /// The error description. /// public string ErrorDescription { get; private set; } /// /// Gets a value indicating whether this instance is error. /// /// /// true if this instance is error; otherwise, false. /// public bool IsError { get; private set; } /// /// Gets or sets a value indicating whether this instance is access denied. /// /// /// true if this instance is access denied; otherwise, false. /// public bool IsAccessDenied { get; set; } /// /// Create failure result /// /// The error description. /// public static DeviceFlowInteractionResult Failure(string errorDescription = null) { return new DeviceFlowInteractionResult { IsError = true, ErrorDescription = errorDescription }; } } ================================================ FILE: src/IdentityServer8/src/Models/DiscoveryDocument.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Models; public class DiscoveryDocument { public string issuer { get; set; } public string jwks_uri { get; set; } public string authorization_endpoint { get; set; } public string token_endpoint { get; set; } public string userinfo_endpoint { get; set; } public string end_session_endpoint { get; set; } public string check_session_iframe { get; set; } public string revocation_endpoint { get; set; } public string introspection_endpoint { get; set; } public bool? frontchannel_logout_supported { get; set; } public bool? frontchannel_logout_session_supported { get; set; } public string[] scopes_supported { get; set; } public string[] claims_supported { get; set; } public string[] response_types_supported { get; set; } public string[] response_modes_supported { get; set; } public string[] grant_types_supported { get; set; } public string[] subject_types_supported { get; set; } public string[] id_token_signing_alg_values_supported { get; set; } public string[] token_endpoint_auth_methods_supported { get; set; } public string[] code_challenge_methods_supported { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/Grant.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models a grant the user has given. /// public class Grant { /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } /// /// Gets or sets the scopes. /// /// /// The scopes. /// public IEnumerable Scopes { get; set; } /// /// Gets or sets the creation time. /// /// /// The creation time. /// public DateTime CreationTime { get; set; } /// /// Gets or sets the expiration. /// /// /// The expiration. /// public DateTime? Expiration { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/GrantTypes.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Models; public class GrantTypes { public static ICollection Implicit => new[] { GrantType.Implicit }; public static ICollection ImplicitAndClientCredentials => new[] { GrantType.Implicit, GrantType.ClientCredentials }; public static ICollection Code => new[] { GrantType.AuthorizationCode }; public static ICollection CodeAndClientCredentials => new[] { GrantType.AuthorizationCode, GrantType.ClientCredentials }; public static ICollection Hybrid => new[] { GrantType.Hybrid }; public static ICollection HybridAndClientCredentials => new[] { GrantType.Hybrid, GrantType.ClientCredentials }; public static ICollection ClientCredentials => new[] { GrantType.ClientCredentials }; public static ICollection ResourceOwnerPassword => new[] { GrantType.ResourceOwnerPassword }; public static ICollection ResourceOwnerPasswordAndClientCredentials => new[] { GrantType.ResourceOwnerPassword, GrantType.ClientCredentials }; public static ICollection DeviceFlow => new[] { GrantType.DeviceFlow }; } ================================================ FILE: src/IdentityServer8/src/Models/IdentityResources.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Convenience class that defines standard identity resources. /// public static class IdentityResources { /// /// Models the standard openid scope /// /// public class OpenId : IdentityResource { /// /// Initializes a new instance of the class. /// public OpenId() { Name = IdentityServerConstants.StandardScopes.OpenId; DisplayName = "Your user identifier"; Required = true; UserClaims.Add(JwtClaimTypes.Subject); } } /// /// Models the standard profile scope /// /// public class Profile : IdentityResource { /// /// Initializes a new instance of the class. /// public Profile() { Name = IdentityServerConstants.StandardScopes.Profile; DisplayName = "User profile"; Description = "Your user profile information (first name, last name, etc.)"; Emphasize = true; UserClaims = Constants.ScopeToClaimsMapping[IdentityServerConstants.StandardScopes.Profile].ToList(); } } /// /// Models the standard email scope /// /// public class Email : IdentityResource { /// /// Initializes a new instance of the class. /// public Email() { Name = IdentityServerConstants.StandardScopes.Email; DisplayName = "Your email address"; Emphasize = true; UserClaims = (Constants.ScopeToClaimsMapping[IdentityServerConstants.StandardScopes.Email].ToList()); } } /// /// Models the standard phone scope /// /// public class Phone : IdentityResource { /// /// Initializes a new instance of the class. /// public Phone() { Name = IdentityServerConstants.StandardScopes.Phone; DisplayName = "Your phone number"; Emphasize = true; UserClaims = Constants.ScopeToClaimsMapping[IdentityServerConstants.StandardScopes.Phone].ToList(); } } /// /// Models the standard address scope /// /// public class Address : IdentityResource { /// /// Initializes a new instance of the class. /// public Address() { Name = IdentityServerConstants.StandardScopes.Address; DisplayName = "Your postal address"; Emphasize = true; UserClaims = Constants.ScopeToClaimsMapping[IdentityServerConstants.StandardScopes.Address].ToList(); } } } ================================================ FILE: src/IdentityServer8/src/Models/JsonWebKey.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Models; public class JsonWebKey { public string kty { get; set; } public string use { get; set; } public string kid { get; set; } public string x5t { get; set; } public string e { get; set; } public string n { get; set; } public string[] x5c { get; set; } public string alg { get; set; } public string x { get; set; } public string y { get; set; } public string crv { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/Messages/AuthorizationRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Represents contextual information about a authorization request. /// public class AuthorizationRequest { /// /// The client. /// public Client Client { get; set; } /// /// The display mode passed from the authorization request. /// /// /// The display mode. /// public string DisplayMode { get; set; } /// /// Gets or sets the redirect URI. /// /// /// The redirect URI. /// public string RedirectUri { get; set; } /// /// The UI locales passed from the authorization request. /// /// /// The UI locales. /// public string UiLocales { get; set; } /// /// The external identity provider requested. This is used to bypass home realm /// discovery (HRD). This is provided via the "idp:" prefix to the acr /// parameter on the authorize request. /// /// /// The external identity provider identifier. /// public string IdP { get; set; } /// /// The tenant requested. This is provided via the "tenant:" prefix to /// the acr parameter on the authorize request. /// /// /// The tenant. /// public string Tenant { get; set; } /// /// The expected username the user will use to login. This is requested from the client /// via the login_hint parameter on the authorize request. /// /// /// The LoginHint. /// public string LoginHint { get; set; } /// /// Gets or sets the collection of prompt modes. /// /// /// The collection of prompt modes. /// public IEnumerable PromptModes { get; set; } = Enumerable.Empty(); /// /// The acr values passed from the authorization request. /// /// /// The acr values. /// public IEnumerable AcrValues { get; set; } /// /// The validated resources. /// public ResourceValidationResult ValidatedResources { get; set; } /// /// Gets the entire parameter collection. /// /// /// The parameters. /// public NameValueCollection Parameters { get; } /// /// Gets the validated contents of the request object (if present) /// /// /// The request object values /// public Dictionary RequestObjectValues { get; } = new Dictionary(); /// /// Initializes a new instance of the class. /// public AuthorizationRequest() { // public for testing Parameters = new NameValueCollection(); } /// /// Initializes a new instance of the class. /// internal AuthorizationRequest(ValidatedAuthorizeRequest request) { Client = request.Client; RedirectUri = request.RedirectUri; DisplayMode = request.DisplayMode; UiLocales = request.UiLocales; IdP = request.GetIdP(); Tenant = request.GetTenant(); LoginHint = request.LoginHint; PromptModes = request.PromptModes; AcrValues = request.GetAcrValues(); ValidatedResources = request.ValidatedResources; Parameters = request.Raw; RequestObjectValues = request.RequestObjectValues; } } ================================================ FILE: src/IdentityServer8/src/Models/Messages/ConsentRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models the parameters to identify a request for consent. /// public class ConsentRequest { /// /// Initializes a new instance of the class. /// /// The request. /// The subject. public ConsentRequest(AuthorizationRequest request, string subject) { ClientId = request.Client.ClientId; Nonce = request.Parameters[OidcConstants.AuthorizeRequest.Nonce]; ScopesRequested = request.Parameters[OidcConstants.AuthorizeRequest.Scope].ParseScopesString(); Subject = subject; } /// /// Initializes a new instance of the class. /// /// The parameters. /// The subject. public ConsentRequest(NameValueCollection parameters, string subject) { ClientId = parameters[OidcConstants.AuthorizeRequest.ClientId]; Nonce = parameters[OidcConstants.AuthorizeRequest.Nonce]; ScopesRequested = parameters[OidcConstants.AuthorizeRequest.Scope].ParseScopesString(); Subject = subject; } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the nonce. /// /// /// The nonce. /// public string Nonce { get; set; } /// /// Gets or sets the scopes requested. /// /// /// The scopes requested. /// public IEnumerable ScopesRequested { get; set; } /// /// Gets or sets the subject. /// /// /// The subject. /// public string Subject { get; set; } /// /// Gets the identifier. /// /// /// The identifier. /// public string Id { get { var normalizedScopes = ScopesRequested?.OrderBy(x => x).Distinct().Aggregate((x, y) => x + "," + y); var value = $"{ClientId}:{Subject}:{Nonce}:{normalizedScopes}"; using (var sha = SHA256.Create()) { var bytes = Encoding.UTF8.GetBytes(value); var hash = sha.ComputeHash(bytes); return Base64Url.Encode(hash); } } } } ================================================ FILE: src/IdentityServer8/src/Models/Messages/ConsentResponse.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models the user's response to the consent screen. /// public class ConsentResponse { /// /// Error, if any, for the consent response. /// public AuthorizationError? Error { get; set; } /// /// Error description. /// public string ErrorDescription { get; set; } /// /// Gets if consent was granted. /// /// /// true if consent was granted; otherwise, false. /// public bool Granted => ScopesValuesConsented != null && ScopesValuesConsented.Any() && Error == null; /// /// Gets or sets the scope values consented to. /// /// /// The scopes. /// public IEnumerable ScopesValuesConsented { get; set; } /// /// Gets or sets a value indicating whether the user wishes the consent to be remembered. /// /// /// true if consent is to be remembered; otherwise, false. /// public bool RememberConsent { get; set; } /// /// Gets the description of the device. /// /// /// The description of the device. /// public string Description { get; set; } } /// /// Enum to model interaction authorization errors. /// public enum AuthorizationError { /// /// Access denied /// AccessDenied, /// /// Interaction required /// InteractionRequired, /// /// Login required /// LoginRequired, /// /// Account selection required /// AccountSelectionRequired, /// /// Consent required /// ConsentRequired } ================================================ FILE: src/IdentityServer8/src/Models/Messages/ErrorMessage.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models the data for the error page. /// public class ErrorMessage { /// /// The display mode passed from the authorization request. /// /// /// The display mode. /// public string DisplayMode { get; set; } /// /// The UI locales passed from the authorization request. /// /// /// The UI locales. /// public string UiLocales { get; set; } /// /// Gets or sets the error code. /// /// /// The error code. /// public string Error { get; set; } /// /// Gets or sets the error description. /// /// /// The error description. /// public string ErrorDescription { get; set; } /// /// The per-request identifier. This can be used to display to the end user and can be used in diagnostics. /// /// /// The request identifier. /// public string RequestId { get; set; } /// /// The redirect URI. /// public string RedirectUri { get; set; } /// /// The response mode. /// public string ResponseMode { get; set; } /// /// The client id making the request (if available). /// public string ClientId { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/Messages/LogoutRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models the validated singout context. /// public class LogoutMessage { /// /// Initializes a new instance of the class. /// public LogoutMessage() { } /// /// Initializes a new instance of the class. /// /// The request. public LogoutMessage(ValidatedEndSessionRequest request) { if (request != null) { if (request.Raw != null) { Parameters = request.Raw.ToFullDictionary(); } // optimize params sent to logout page, since we'd like to send them in URL (not as cookie) Parameters.Remove(OidcConstants.EndSessionRequest.IdTokenHint); Parameters.Remove(OidcConstants.EndSessionRequest.PostLogoutRedirectUri); Parameters.Remove(OidcConstants.EndSessionRequest.State); ClientId = request.Client?.ClientId; ClientName = request.Client?.ClientName; SubjectId = request.Subject?.GetSubjectId(); SessionId = request.SessionId; ClientIds = request.ClientIds; if (request.PostLogOutUri != null) { PostLogoutRedirectUri = request.PostLogOutUri; if (request.State != null) { PostLogoutRedirectUri = PostLogoutRedirectUri.AddQueryString(OidcConstants.EndSessionRequest.State, request.State); } } } } /// /// Gets or sets the client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the client name. /// public string ClientName { get; set; } /// /// Gets or sets the post logout redirect URI. /// public string PostLogoutRedirectUri { get; set; } /// /// Gets or sets the subject identifier for the user at logout time. /// public string SubjectId { get; set; } /// /// Gets or sets the session identifier for the user at logout time. /// public string SessionId { get; set; } /// /// Ids of clients known to have an authentication session for user at end session time /// public IEnumerable ClientIds { get; set; } /// /// Gets the entire parameter collection. /// public IDictionary Parameters { get; set; } = new Dictionary(); /// /// Flag to indicate if the payload contains useful information or not to avoid serailization. /// internal bool ContainsPayload => ClientId.IsPresent() || ClientIds?.Any() == true; } /// /// Models the request from a client to sign the user out. /// public class LogoutRequest { /// /// Initializes a new instance of the class. /// /// The iframe URL. /// The message. public LogoutRequest(string iframeUrl, LogoutMessage message) { if (message != null) { ClientId = message.ClientId; ClientName = message.ClientName; PostLogoutRedirectUri = message.PostLogoutRedirectUri; SubjectId = message.SubjectId; SessionId = message.SessionId; ClientIds = message.ClientIds; Parameters = message.Parameters.FromFullDictionary(); } SignOutIFrameUrl = iframeUrl; } /// /// Gets or sets the client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the client name. /// public string ClientName { get; set; } /// /// Gets or sets the post logout redirect URI. /// public string PostLogoutRedirectUri { get; set; } /// /// Gets or sets the subject identifier for the user at logout time. /// public string SubjectId { get; set; } /// /// Gets or sets the session identifier for the user at logout time. /// public string SessionId { get; set; } /// /// Ids of clients known to have an authentication session for user at end session time /// public IEnumerable ClientIds { get; set; } /// /// Gets the entire parameter collection. /// public NameValueCollection Parameters { get; } = new NameValueCollection(); /// /// Gets or sets the sign out iframe URL. /// /// /// The sign out iframe URL. /// public string SignOutIFrameUrl { get; set; } /// /// Gets or sets a value indicating whether the user should be prompted for signout. /// /// /// true if the signout prompt should be shown; otherwise, false. /// public bool ShowSignoutPrompt => ClientId.IsMissing(); } ================================================ FILE: src/IdentityServer8/src/Models/Messages/Message.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Base class for data that needs to be written out as cookies. /// public class Message { /// /// Should only be used from unit tests /// /// internal Message(TModel data) : this(data, DateTime.UtcNow) { } /// /// For JSON serializer. /// System.Text.Json.JsonSerializer requires public, parameterless constructor /// public Message() { } /// /// Initializes a new instance of the class. /// /// The data. /// The current UTC date/time. public Message(TModel data, DateTime now) { Created = now.Ticks; Data = data; } /// /// Gets or sets the UTC ticks the was created. /// /// /// The created UTC ticks. /// public long Created { get; set; } /// /// Gets or sets the data. /// /// /// The data. /// public TModel Data { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/ParsedSecret.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Represents a secret extracted from the HttpContext /// public class ParsedSecret { /// /// Gets or sets the identifier associated with this secret /// /// /// The identifier. /// public string Id { get; set; } /// /// Gets or sets the credential to verify the secret /// /// /// The credential. /// public object Credential { get; set; } /// /// Gets or sets the type of the secret /// /// /// The type. /// public string Type { get; set; } /// /// Gets or sets additional properties. /// /// /// The properties. /// public Dictionary Properties { get; set; } = new Dictionary(); } ================================================ FILE: src/IdentityServer8/src/Models/SecurityKeyInfo.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Models; /// /// Information about a security key /// public class SecurityKeyInfo { /// /// The key /// public SecurityKey Key { get; set; } /// /// The signing algorithm /// public string SigningAlgorithm { get; set; } } ================================================ FILE: src/IdentityServer8/src/Models/TokenCreationRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models the data to create a token from a validated request. /// public class TokenCreationRequest { /// /// Gets or sets the subject. /// /// /// The subject. /// public ClaimsPrincipal Subject { get; set; } /// /// Gets or sets the validated resources. /// /// /// The resources. /// public ResourceValidationResult ValidatedResources { get; set; } /// /// Gets or sets the validated request. /// /// /// The validated request. /// public ValidatedRequest ValidatedRequest { get; set; } /// /// Gets or sets a value indicating whether [include all identity claims]. /// /// /// true if [include all identity claims]; otherwise, false. /// public bool IncludeAllIdentityClaims { get; set; } /// /// Gets or sets the access token to hash. /// /// /// The access token to hash. /// public string AccessTokenToHash { get; set; } /// /// Gets or sets the authorization code to hash. /// /// /// The authorization code to hash. /// public string AuthorizationCodeToHash { get; set; } /// /// Gets or sets pre-hashed state /// /// /// The pre-hashed state /// public string StateHash { get; set; } /// /// Gets or sets the nonce. /// /// /// The nonce. /// public string Nonce { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } /// /// Called to validate the before it is processed. /// public void Validate() { if (ValidatedResources == null) throw new ArgumentNullException(nameof(ValidatedResources)); if (ValidatedRequest == null) throw new ArgumentNullException(nameof(ValidatedRequest)); } } ================================================ FILE: src/IdentityServer8/src/Models/TokenRequestErrors.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Token request errors /// public enum TokenRequestErrors { /// /// invalid_request /// InvalidRequest, /// /// invalid_client /// InvalidClient, /// /// invalid_grant /// InvalidGrant, /// /// unauthorized_client /// UnauthorizedClient, /// /// unsupported_grant_type /// UnsupportedGrantType, /// /// invalid_scope /// InvalidScope, /// /// invalid_target /// InvalidTarget } ================================================ FILE: src/IdentityServer8/src/Properties/AssemblyInfo.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ [assembly: InternalsVisibleTo("IdentityServer.UnitTests, PublicKey = 002400000480000094000000060200000024000052534131000400000100010057b24455efc2a317afb0644a2169c05644e439985c42cf4eb98706779651801add1da073da8b5e253e8d4335d59b3197bb941ebe943c63f7efbc3005c428f0d69b809e86bdc828fa431fae4b71005f26b52a26a3ee5cf0f6fdf744d4534a7a503683123f58e1082828b018245d2e40d8542f72a623c01490d73a5d3ff94a88c5")] [assembly: InternalsVisibleTo("IdentityServer.IntegrationTests, PublicKey = 002400000480000094000000060200000024000052534131000400000100010057b24455efc2a317afb0644a2169c05644e439985c42cf4eb98706779651801add1da073da8b5e253e8d4335d59b3197bb941ebe943c63f7efbc3005c428f0d69b809e86bdc828fa431fae4b71005f26b52a26a3ee5cf0f6fdf744d4534a7a503683123f58e1082828b018245d2e40d8542f72a623c01490d73a5d3ff94a88c5")] ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Default/AuthorizeInteractionResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Default logic for determining if user must login or consent when making requests to the authorization endpoint. /// /// public class AuthorizeInteractionResponseGenerator : IAuthorizeInteractionResponseGenerator { /// /// The logger. /// protected readonly ILogger Logger; /// /// The consent service. /// protected readonly IConsentService Consent; /// /// The profile service. /// protected readonly IProfileService Profile; /// /// The clock /// protected readonly ISystemClock Clock; /// /// Initializes a new instance of the class. /// /// The clock. /// The logger. /// The consent. /// The profile. public AuthorizeInteractionResponseGenerator( ISystemClock clock, ILogger logger, IConsentService consent, IProfileService profile) { Clock = clock; Logger = logger; Consent = consent; Profile = profile; } /// /// Processes the interaction logic. /// /// The request. /// The consent. /// public virtual async Task ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null) { Logger.LogTrace("ProcessInteractionAsync"); if (consent != null && consent.Granted == false && consent.Error.HasValue && request.Subject.IsAuthenticated() == false) { // special case when anonymous user has issued an error prior to authenticating Logger.LogInformation("Error: User consent result: {error}", consent.Error); var error = consent.Error switch { AuthorizationError.AccountSelectionRequired => OidcConstants.AuthorizeErrors.AccountSelectionRequired, AuthorizationError.ConsentRequired => OidcConstants.AuthorizeErrors.ConsentRequired, AuthorizationError.InteractionRequired => OidcConstants.AuthorizeErrors.InteractionRequired, AuthorizationError.LoginRequired => OidcConstants.AuthorizeErrors.LoginRequired, _ => OidcConstants.AuthorizeErrors.AccessDenied }; return new InteractionResponse { Error = error, ErrorDescription = consent.ErrorDescription }; } var result = await ProcessLoginAsync(request); if (!result.IsLogin && !result.IsError && !result.IsRedirect) { result = await ProcessConsentAsync(request, consent); } if ((result.IsLogin || result.IsConsent || result.IsRedirect) && request.PromptModes.Contains(OidcConstants.PromptModes.None)) { // prompt=none means do not show the UI Logger.LogInformation("Changing response to LoginRequired: prompt=none was requested"); result = new InteractionResponse { Error = result.IsLogin ? OidcConstants.AuthorizeErrors.LoginRequired : result.IsConsent ? OidcConstants.AuthorizeErrors.ConsentRequired : OidcConstants.AuthorizeErrors.InteractionRequired }; } return result; } /// /// Processes the login logic. /// /// The request. /// protected internal virtual async Task ProcessLoginAsync(ValidatedAuthorizeRequest request) { if (request.PromptModes.Contains(OidcConstants.PromptModes.Login) || request.PromptModes.Contains(OidcConstants.PromptModes.SelectAccount)) { Logger.LogInformation("Showing login: request contains prompt={0}", request.PromptModes.ToSpaceSeparatedString()); // remove prompt so when we redirect back in from login page // we won't think we need to force a prompt again request.RemovePrompt(); return new InteractionResponse { IsLogin = true }; } // unauthenticated user var isAuthenticated = request.Subject.IsAuthenticated(); // user de-activated bool isActive = false; if (isAuthenticated) { var isActiveCtx = new IsActiveContext(request.Subject, request.Client, IdentityServerConstants.ProfileIsActiveCallers.AuthorizeEndpoint); await Profile.IsActiveAsync(isActiveCtx); isActive = isActiveCtx.IsActive; } if (!isAuthenticated || !isActive) { if (!isAuthenticated) { Logger.LogInformation("Showing login: User is not authenticated"); } else if (!isActive) { Logger.LogInformation("Showing login: User is not active"); } return new InteractionResponse { IsLogin = true }; } // check current idp var currentIdp = request.Subject.GetIdentityProvider(); // check if idp login hint matches current provider var idp = request.GetIdP(); if (idp.IsPresent()) { if (idp != currentIdp) { Logger.LogInformation("Showing login: Current IdP ({currentIdp}) is not the requested IdP ({idp})", currentIdp, idp); return new InteractionResponse { IsLogin = true }; } } // check authentication freshness if (request.MaxAge.HasValue) { var authTime = request.Subject.GetAuthenticationTime(); if (Clock.UtcNow > authTime.AddSeconds(request.MaxAge.Value)) { Logger.LogInformation("Showing login: Requested MaxAge exceeded."); return new InteractionResponse { IsLogin = true }; } } // check local idp restrictions if (currentIdp == IdentityServerConstants.LocalIdentityProvider) { if (!request.Client.EnableLocalLogin) { Logger.LogInformation("Showing login: User logged in locally, but client does not allow local logins"); return new InteractionResponse { IsLogin = true }; } } // check external idp restrictions if user not using local idp else if (request.Client.IdentityProviderRestrictions != null && request.Client.IdentityProviderRestrictions.Any() && !request.Client.IdentityProviderRestrictions.Contains(currentIdp)) { Logger.LogInformation("Showing login: User is logged in with idp: {idp}, but idp not in client restriction list.", currentIdp); return new InteractionResponse { IsLogin = true }; } // check client's user SSO timeout if (request.Client.UserSsoLifetime.HasValue) { var authTimeEpoch = request.Subject.GetAuthenticationTimeEpoch(); var nowEpoch = Clock.UtcNow.ToUnixTimeSeconds(); var diff = nowEpoch - authTimeEpoch; if (diff > request.Client.UserSsoLifetime.Value) { Logger.LogInformation("Showing login: User's auth session duration: {sessionDuration} exceeds client's user SSO lifetime: {userSsoLifetime}.", diff, request.Client.UserSsoLifetime); return new InteractionResponse { IsLogin = true }; } } return new InteractionResponse(); } /// /// Processes the consent logic. /// /// The request. /// The consent. /// /// /// Invalid PromptMode protected internal virtual async Task ProcessConsentAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null) { if (request == null) throw new ArgumentNullException(nameof(request)); if (request.PromptModes.Any() && !request.PromptModes.Contains(OidcConstants.PromptModes.None) && !request.PromptModes.Contains(OidcConstants.PromptModes.Consent)) { Logger.LogError("Invalid prompt mode: {promptMode}", request.PromptModes.ToSpaceSeparatedString()); throw new ArgumentException("Invalid PromptMode"); } var consentRequired = await Consent.RequiresConsentAsync(request.Subject, request.Client, request.ValidatedResources.ParsedScopes); if (consentRequired && request.PromptModes.Contains(OidcConstants.PromptModes.None)) { Logger.LogInformation("Error: prompt=none requested, but consent is required."); return new InteractionResponse { Error = OidcConstants.AuthorizeErrors.ConsentRequired }; } if (request.PromptModes.Contains(OidcConstants.PromptModes.Consent) || consentRequired) { var response = new InteractionResponse(); // did user provide consent if (consent == null) { // user was not yet shown conset screen response.IsConsent = true; Logger.LogInformation("Showing consent: User has not yet consented"); } else { request.WasConsentShown = true; Logger.LogTrace("Consent was shown to user"); // user was shown consent -- did they say yes or no if (consent.Granted == false) { // no need to show consent screen again // build error to return to client Logger.LogInformation("Error: User consent result: {error}", consent.Error); var error = consent.Error switch { AuthorizationError.AccountSelectionRequired => OidcConstants.AuthorizeErrors.AccountSelectionRequired, AuthorizationError.ConsentRequired => OidcConstants.AuthorizeErrors.ConsentRequired, AuthorizationError.InteractionRequired => OidcConstants.AuthorizeErrors.InteractionRequired, AuthorizationError.LoginRequired => OidcConstants.AuthorizeErrors.LoginRequired, _ => OidcConstants.AuthorizeErrors.AccessDenied }; response.Error = error; response.ErrorDescription = consent.ErrorDescription; } else { // double check that required scopes are in the list of consented scopes var requiredScopes = request.ValidatedResources.GetRequiredScopeValues(); var valid = requiredScopes.All(x => consent.ScopesValuesConsented.Contains(x)); if (valid == false) { response.Error = OidcConstants.AuthorizeErrors.AccessDenied; Logger.LogInformation("Error: User denied consent to required scopes"); } else { // they said yes, set scopes they chose request.Description = consent.Description; request.ValidatedResources = request.ValidatedResources.Filter(consent.ScopesValuesConsented); Logger.LogInformation("User consented to scopes: {scopes}", consent.ScopesValuesConsented); if (request.Client.AllowRememberConsent) { // remember consent var parsedScopes = Enumerable.Empty(); if (consent.RememberConsent) { // remember what user actually selected parsedScopes = request.ValidatedResources.ParsedScopes; Logger.LogDebug("User indicated to remember consent for scopes: {scopes}", request.ValidatedResources.RawScopeValues); } await Consent.UpdateConsentAsync(request.Subject, request.Client, parsedScopes); } } } } return response; } return new InteractionResponse(); } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Default/AuthorizeResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// The authorize response generator /// /// public class AuthorizeResponseGenerator : IAuthorizeResponseGenerator { /// /// The token service /// protected readonly ITokenService TokenService; /// /// The authorization code store /// protected readonly IAuthorizationCodeStore AuthorizationCodeStore; /// /// The event service /// protected readonly IEventService Events; /// /// The logger /// protected readonly ILogger Logger; /// /// The clock /// protected readonly ISystemClock Clock; /// /// The key material service /// protected readonly IKeyMaterialService KeyMaterialService; /// /// Initializes a new instance of the class. /// /// The clock. /// The logger. /// The token service. /// /// The authorization code store. /// The events. public AuthorizeResponseGenerator( ISystemClock clock, ITokenService tokenService, IKeyMaterialService keyMaterialService, IAuthorizationCodeStore authorizationCodeStore, ILogger logger, IEventService events) { Clock = clock; TokenService = tokenService; KeyMaterialService = keyMaterialService; AuthorizationCodeStore = authorizationCodeStore; Events = events; Logger = logger; } /// /// Creates the response /// /// The request. /// /// invalid grant type: " + request.GrantType public virtual async Task CreateResponseAsync(ValidatedAuthorizeRequest request) { if (request.GrantType == GrantType.AuthorizationCode) { return await CreateCodeFlowResponseAsync(request); } if (request.GrantType == GrantType.Implicit) { return await CreateImplicitFlowResponseAsync(request); } if (request.GrantType == GrantType.Hybrid) { return await CreateHybridFlowResponseAsync(request); } Logger.LogError("Unsupported grant type: " + request.GrantType); throw new InvalidOperationException("invalid grant type: " + request.GrantType); } /// /// Creates the response for a hybrid flow request /// /// /// protected virtual async Task CreateHybridFlowResponseAsync(ValidatedAuthorizeRequest request) { Logger.LogDebug("Creating Hybrid Flow response."); var code = await CreateCodeAsync(request); var id = await AuthorizationCodeStore.StoreAuthorizationCodeAsync(code); var response = await CreateImplicitFlowResponseAsync(request, id); response.Code = id; return response; } /// /// Creates the response for a code flow request /// /// /// protected virtual async Task CreateCodeFlowResponseAsync(ValidatedAuthorizeRequest request) { Logger.LogDebug("Creating Authorization Code Flow response."); var code = await CreateCodeAsync(request); var id = await AuthorizationCodeStore.StoreAuthorizationCodeAsync(code); var response = new AuthorizeResponse { Request = request, Code = id, SessionState = request.GenerateSessionStateValue() }; return response; } /// /// Creates the response for a implicit flow request /// /// /// /// protected virtual async Task CreateImplicitFlowResponseAsync(ValidatedAuthorizeRequest request, string authorizationCode = null) { Logger.LogDebug("Creating Implicit Flow response."); string accessTokenValue = null; int accessTokenLifetime = 0; var responseTypes = request.ResponseType.FromSpaceSeparatedString(); if (responseTypes.Contains(OidcConstants.ResponseTypes.Token)) { var tokenRequest = new TokenCreationRequest { Subject = request.Subject, ValidatedResources = request.ValidatedResources, ValidatedRequest = request }; var accessToken = await TokenService.CreateAccessTokenAsync(tokenRequest); accessTokenLifetime = accessToken.Lifetime; accessTokenValue = await TokenService.CreateSecurityTokenAsync(accessToken); } string jwt = null; if (responseTypes.Contains(OidcConstants.ResponseTypes.IdToken)) { string stateHash = null; if (request.State.IsPresent()) { var credential = await KeyMaterialService.GetSigningCredentialsAsync(request.Client.AllowedIdentityTokenSigningAlgorithms); if (credential == null) { throw new InvalidOperationException("No signing credential is configured."); } var algorithm = credential.Algorithm; stateHash = CryptoHelper.CreateHashClaimValue(request.State, algorithm); } var tokenRequest = new TokenCreationRequest { ValidatedRequest = request, Subject = request.Subject, ValidatedResources = request.ValidatedResources, Nonce = request.Raw.Get(OidcConstants.AuthorizeRequest.Nonce), IncludeAllIdentityClaims = !request.AccessTokenRequested, AccessTokenToHash = accessTokenValue, AuthorizationCodeToHash = authorizationCode, StateHash = stateHash }; var idToken = await TokenService.CreateIdentityTokenAsync(tokenRequest); jwt = await TokenService.CreateSecurityTokenAsync(idToken); } var response = new AuthorizeResponse { Request = request, AccessToken = accessTokenValue, AccessTokenLifetime = accessTokenLifetime, IdentityToken = jwt, SessionState = request.GenerateSessionStateValue() }; return response; } /// /// Creates an authorization code /// /// /// protected virtual async Task CreateCodeAsync(ValidatedAuthorizeRequest request) { string stateHash = null; if (request.State.IsPresent()) { var credential = await KeyMaterialService.GetSigningCredentialsAsync(request.Client.AllowedIdentityTokenSigningAlgorithms); if (credential == null) { throw new InvalidOperationException("No signing credential is configured."); } var algorithm = credential.Algorithm; stateHash = CryptoHelper.CreateHashClaimValue(request.State, algorithm); } var code = new AuthorizationCode { CreationTime = Clock.UtcNow.UtcDateTime, ClientId = request.Client.ClientId, Lifetime = request.Client.AuthorizationCodeLifetime, Subject = request.Subject, SessionId = request.SessionId, Description = request.Description, CodeChallenge = request.CodeChallenge.Sha256(), CodeChallengeMethod = request.CodeChallengeMethod, IsOpenId = request.IsOpenIdRequest, RequestedScopes = request.ValidatedResources.RawScopeValues, RedirectUri = request.RedirectUri, Nonce = request.Nonce, StateHash = stateHash, WasConsentShown = request.WasConsentShown }; return code; } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Default/DeviceAuthorizationResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// The device authorizaiton response generator /// /// public class DeviceAuthorizationResponseGenerator : IDeviceAuthorizationResponseGenerator { /// /// The options /// protected readonly IdentityServerOptions Options; /// /// The user code service /// protected readonly IUserCodeService UserCodeService; /// /// The device flow code service /// protected readonly IDeviceFlowCodeService DeviceFlowCodeService; /// /// The clock /// protected readonly ISystemClock Clock; /// /// The logger /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The options. /// The user code service. /// The device flow code service. /// The clock. /// The logger. public DeviceAuthorizationResponseGenerator(IdentityServerOptions options, IUserCodeService userCodeService, IDeviceFlowCodeService deviceFlowCodeService, ISystemClock clock, ILogger logger) { Options = options; UserCodeService = userCodeService; DeviceFlowCodeService = deviceFlowCodeService; Clock = clock; Logger = logger; } /// /// Processes the response. /// /// The validation result. /// The base URL. /// /// validationResult or Client /// Value cannot be null or whitespace. - baseUrl public virtual async Task ProcessAsync(DeviceAuthorizationRequestValidationResult validationResult, string baseUrl) { if (validationResult == null) throw new ArgumentNullException(nameof(validationResult)); if (validationResult.ValidatedRequest.Client == null) throw new ArgumentNullException(nameof(validationResult.ValidatedRequest.Client)); if (string.IsNullOrWhiteSpace(baseUrl)) throw new ArgumentException("Value cannot be null or whitespace.", nameof(baseUrl)); Logger.LogTrace("Creating response for device authorization request"); var response = new DeviceAuthorizationResponse(); // generate user_code var userCodeGenerator = await UserCodeService.GetGenerator( validationResult.ValidatedRequest.Client.UserCodeType ?? Options.DeviceFlow.DefaultUserCodeType); var retryCount = 0; while (retryCount < userCodeGenerator.RetryLimit) { var userCode = await userCodeGenerator.GenerateAsync(); var deviceCode = await DeviceFlowCodeService.FindByUserCodeAsync(userCode); if (deviceCode == null) { response.UserCode = userCode; break; } retryCount++; } if (response.UserCode == null) { throw new InvalidOperationException("Unable to create unique device flow user code"); } // generate verification URIs response.VerificationUri = Options.UserInteraction.DeviceVerificationUrl; if (response.VerificationUri.IsLocalUrl()) { // if url is relative, parse absolute URL response.VerificationUri = baseUrl.RemoveTrailingSlash() + Options.UserInteraction.DeviceVerificationUrl; } if (!string.IsNullOrWhiteSpace(Options.UserInteraction.DeviceVerificationUserCodeParameter)) { response.VerificationUriComplete = $"{response.VerificationUri}?{Options.UserInteraction.DeviceVerificationUserCodeParameter}={response.UserCode}"; } // expiration response.DeviceCodeLifetime = validationResult.ValidatedRequest.Client.DeviceCodeLifetime; // interval response.Interval = Options.DeviceFlow.Interval; // store device request (device code & user code) response.DeviceCode = await DeviceFlowCodeService.StoreDeviceAuthorizationAsync(response.UserCode, new DeviceCode { Description = validationResult.ValidatedRequest.Description, ClientId = validationResult.ValidatedRequest.Client.ClientId, IsOpenId = validationResult.ValidatedRequest.IsOpenIdRequest, Lifetime = response.DeviceCodeLifetime, CreationTime = Clock.UtcNow.UtcDateTime, RequestedScopes = validationResult.ValidatedRequest.ValidatedResources.RawScopeValues }); return response; } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Default/DiscoveryResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; using JsonWebKey = Microsoft.IdentityModel.Tokens.JsonWebKey; namespace IdentityServer8.ResponseHandling; /// /// Default implementation of the discovery endpoint response generator /// /// public class DiscoveryResponseGenerator : IDiscoveryResponseGenerator { /// /// The options /// protected readonly IdentityServerOptions Options; /// /// The extension grants validator /// protected readonly ExtensionGrantValidator ExtensionGrants; /// /// The key material service /// protected readonly IKeyMaterialService Keys; /// /// The resource owner validator /// protected readonly IResourceOwnerPasswordValidator ResourceOwnerValidator; /// /// The resource store /// protected readonly IResourceStore ResourceStore; /// /// The secret parsers /// protected readonly ISecretsListParser SecretParsers; /// /// The logger /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The options. /// The resource store. /// The keys. /// The extension grants. /// The secret parsers. /// The resource owner validator. /// The logger. public DiscoveryResponseGenerator( IdentityServerOptions options, IResourceStore resourceStore, IKeyMaterialService keys, ExtensionGrantValidator extensionGrants, ISecretsListParser secretParsers, IResourceOwnerPasswordValidator resourceOwnerValidator, ILogger logger) { Options = options; ResourceStore = resourceStore; Keys = keys; ExtensionGrants = extensionGrants; SecretParsers = secretParsers; ResourceOwnerValidator = resourceOwnerValidator; Logger = logger; } /// /// Creates the discovery document. /// /// The base URL. /// The issuer URI. public virtual async Task> CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri) { var entries = new Dictionary { { OidcConstants.Discovery.Issuer, issuerUri } }; // jwks if (Options.Discovery.ShowKeySet) { if ((await Keys.GetValidationKeysAsync()).Any()) { entries.Add(OidcConstants.Discovery.JwksUri, baseUrl + Constants.ProtocolRoutePaths.DiscoveryWebKeys); } } // endpoints if (Options.Discovery.ShowEndpoints) { if (Options.Endpoints.EnableAuthorizeEndpoint) { entries.Add(OidcConstants.Discovery.AuthorizationEndpoint, baseUrl + Constants.ProtocolRoutePaths.Authorize); } if (Options.Endpoints.EnableTokenEndpoint) { entries.Add(OidcConstants.Discovery.TokenEndpoint, baseUrl + Constants.ProtocolRoutePaths.Token); } if (Options.Endpoints.EnableUserInfoEndpoint) { entries.Add(OidcConstants.Discovery.UserInfoEndpoint, baseUrl + Constants.ProtocolRoutePaths.UserInfo); } if (Options.Endpoints.EnableEndSessionEndpoint) { entries.Add(OidcConstants.Discovery.EndSessionEndpoint, baseUrl + Constants.ProtocolRoutePaths.EndSession); } if (Options.Endpoints.EnableCheckSessionEndpoint) { entries.Add(OidcConstants.Discovery.CheckSessionIframe, baseUrl + Constants.ProtocolRoutePaths.CheckSession); } if (Options.Endpoints.EnableTokenRevocationEndpoint) { entries.Add(OidcConstants.Discovery.RevocationEndpoint, baseUrl + Constants.ProtocolRoutePaths.Revocation); } if (Options.Endpoints.EnableIntrospectionEndpoint) { entries.Add(OidcConstants.Discovery.IntrospectionEndpoint, baseUrl + Constants.ProtocolRoutePaths.Introspection); } if (Options.Endpoints.EnableDeviceAuthorizationEndpoint) { entries.Add(OidcConstants.Discovery.DeviceAuthorizationEndpoint, baseUrl + Constants.ProtocolRoutePaths.DeviceAuthorization); } if (Options.MutualTls.Enabled) { var mtlsEndpoints = new Dictionary(); if (Options.Endpoints.EnableTokenEndpoint) { mtlsEndpoints.Add(OidcConstants.Discovery.TokenEndpoint, ConstructMtlsEndpoint(Constants.ProtocolRoutePaths.Token)); } if (Options.Endpoints.EnableTokenRevocationEndpoint) { mtlsEndpoints.Add(OidcConstants.Discovery.RevocationEndpoint, ConstructMtlsEndpoint(Constants.ProtocolRoutePaths.Revocation)); } if (Options.Endpoints.EnableIntrospectionEndpoint) { mtlsEndpoints.Add(OidcConstants.Discovery.IntrospectionEndpoint, ConstructMtlsEndpoint(Constants.ProtocolRoutePaths.Introspection)); } if (Options.Endpoints.EnableDeviceAuthorizationEndpoint) { mtlsEndpoints.Add(OidcConstants.Discovery.DeviceAuthorizationEndpoint, ConstructMtlsEndpoint(Constants.ProtocolRoutePaths.DeviceAuthorization)); } if (mtlsEndpoints.Any()) { entries.Add(OidcConstants.Discovery.MtlsEndpointAliases, mtlsEndpoints); } string ConstructMtlsEndpoint(string endpoint) { // path based if (Options.MutualTls.DomainName.IsMissing()) { return baseUrl + endpoint.Replace(Constants.ProtocolRoutePaths.ConnectPathPrefix, Constants.ProtocolRoutePaths.MtlsPathPrefix); } // domain based if (Options.MutualTls.DomainName.Contains(".")) { return $"https://{Options.MutualTls.DomainName}/{endpoint}"; } // sub-domain based else { var parts = baseUrl.Split("://"); return $"https://{Options.MutualTls.DomainName}.{parts[1]}{endpoint}"; } } } } // logout if (Options.Endpoints.EnableEndSessionEndpoint) { entries.Add(OidcConstants.Discovery.FrontChannelLogoutSupported, true); entries.Add(OidcConstants.Discovery.FrontChannelLogoutSessionSupported, true); entries.Add(OidcConstants.Discovery.BackChannelLogoutSupported, true); entries.Add(OidcConstants.Discovery.BackChannelLogoutSessionSupported, true); } // scopes and claims if (Options.Discovery.ShowIdentityScopes || Options.Discovery.ShowApiScopes || Options.Discovery.ShowClaims) { var resources = await ResourceStore.GetAllEnabledResourcesAsync(); var scopes = new List(); // scopes if (Options.Discovery.ShowIdentityScopes) { scopes.AddRange(resources.IdentityResources.Where(x => x.ShowInDiscoveryDocument).Select(x => x.Name)); } if (Options.Discovery.ShowApiScopes) { var apiScopes = from scope in resources.ApiScopes where scope.ShowInDiscoveryDocument select scope.Name; scopes.AddRange(apiScopes); scopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } if (scopes.Any()) { entries.Add(OidcConstants.Discovery.ScopesSupported, scopes.ToArray()); } // claims if (Options.Discovery.ShowClaims) { var claims = new List(); // add non-hidden identity scopes related claims claims.AddRange(resources.IdentityResources.Where(x => x.ShowInDiscoveryDocument).SelectMany(x => x.UserClaims)); claims.AddRange(resources.ApiResources.Where(x => x.ShowInDiscoveryDocument).SelectMany(x => x.UserClaims)); claims.AddRange(resources.ApiScopes.Where(x => x.ShowInDiscoveryDocument).SelectMany(x => x.UserClaims)); entries.Add(OidcConstants.Discovery.ClaimsSupported, claims.Distinct().ToArray()); } } // grant types if (Options.Discovery.ShowGrantTypes) { var standardGrantTypes = new List { OidcConstants.GrantTypes.AuthorizationCode, OidcConstants.GrantTypes.ClientCredentials, OidcConstants.GrantTypes.RefreshToken, OidcConstants.GrantTypes.Implicit }; if (!(ResourceOwnerValidator is NotSupportedResourceOwnerPasswordValidator)) { standardGrantTypes.Add(OidcConstants.GrantTypes.Password); } if (Options.Endpoints.EnableDeviceAuthorizationEndpoint) { standardGrantTypes.Add(OidcConstants.GrantTypes.DeviceCode); } var showGrantTypes = new List(standardGrantTypes); if (Options.Discovery.ShowExtensionGrantTypes) { showGrantTypes.AddRange(ExtensionGrants.GetAvailableGrantTypes()); } entries.Add(OidcConstants.Discovery.GrantTypesSupported, showGrantTypes.ToArray()); } // response types if (Options.Discovery.ShowResponseTypes) { entries.Add(OidcConstants.Discovery.ResponseTypesSupported, Constants.SupportedResponseTypes.ToArray()); } // response modes if (Options.Discovery.ShowResponseModes) { entries.Add(OidcConstants.Discovery.ResponseModesSupported, Constants.SupportedResponseModes.ToArray()); } // misc if (Options.Discovery.ShowTokenEndpointAuthenticationMethods) { var types = SecretParsers.GetAvailableAuthenticationMethods().ToList(); if (Options.MutualTls.Enabled) { types.Add(OidcConstants.EndpointAuthenticationMethods.TlsClientAuth); types.Add(OidcConstants.EndpointAuthenticationMethods.SelfSignedTlsClientAuth); } entries.Add(OidcConstants.Discovery.TokenEndpointAuthenticationMethodsSupported, types); } var signingCredentials = await Keys.GetAllSigningCredentialsAsync(); if (signingCredentials.Any()) { var signingAlgorithms = signingCredentials.Select(c => c.Algorithm).Distinct(); entries.Add(OidcConstants.Discovery.IdTokenSigningAlgorithmsSupported, signingAlgorithms); } entries.Add(OidcConstants.Discovery.SubjectTypesSupported, new[] { "public" }); entries.Add(OidcConstants.Discovery.CodeChallengeMethodsSupported, new[] { OidcConstants.CodeChallengeMethods.Plain, OidcConstants.CodeChallengeMethods.Sha256 }); if (Options.Endpoints.EnableAuthorizeEndpoint) { entries.Add(OidcConstants.Discovery.RequestParameterSupported, true); if (Options.Endpoints.EnableJwtRequestUri) { entries.Add(OidcConstants.Discovery.RequestUriParameterSupported, true); } } if (Options.MutualTls.Enabled) { entries.Add(OidcConstants.Discovery.TlsClientCertificateBoundAccessTokens, true); } // custom entries if (!Options.Discovery.CustomEntries.EnumerableIsNullOrEmpty()) { foreach ((string key, object value) in Options.Discovery.CustomEntries) { if (entries.ContainsKey(key)) { Logger.LogError("Discovery custom entry {key} cannot be added, because it already exists.", key); } else { if (value is string customValueString) { if (customValueString.StartsWith("~/") && Options.Discovery.ExpandRelativePathsInCustomEntries) { entries.Add(key, baseUrl + customValueString.Substring(2)); continue; } } entries.Add(key, value); } } } return entries; } /// /// Creates the JWK document. /// public virtual async Task> CreateJwkDocumentAsync() { var webKeys = new List(); foreach (var key in await Keys.GetValidationKeysAsync()) { if (key.Key is X509SecurityKey x509Key) { var cert64 = Convert.ToBase64String(x509Key.Certificate.RawData); var thumbprint = Base64Url.Encode(x509Key.Certificate.GetCertHash()); if (x509Key.PublicKey is RSA rsa) { var parameters = rsa.ExportParameters(false); var exponent = Base64Url.Encode(parameters.Exponent); var modulus = Base64Url.Encode(parameters.Modulus); var rsaJsonWebKey = new Models.JsonWebKey { kty = "RSA", use = "sig", kid = x509Key.KeyId, x5t = thumbprint, e = exponent, n = modulus, x5c = new[] { cert64 }, alg = key.SigningAlgorithm }; webKeys.Add(rsaJsonWebKey); } else if (x509Key.PublicKey is ECDsa ecdsa) { var parameters = ecdsa.ExportParameters(false); var x = Base64Url.Encode(parameters.Q.X); var y = Base64Url.Encode(parameters.Q.Y); var ecdsaJsonWebKey = new Models.JsonWebKey { kty = "EC", use = "sig", kid = x509Key.KeyId, x5t = thumbprint, x = x, y = y, crv = CryptoHelper.GetCrvValueFromCurve(parameters.Curve), x5c = new[] { cert64 }, alg = key.SigningAlgorithm }; webKeys.Add(ecdsaJsonWebKey); } else { throw new InvalidOperationException($"key type: {x509Key.PublicKey.GetType().Name} not supported."); } } else if (key.Key is RsaSecurityKey rsaKey) { var parameters = rsaKey.Rsa?.ExportParameters(false) ?? rsaKey.Parameters; var exponent = Base64Url.Encode(parameters.Exponent); var modulus = Base64Url.Encode(parameters.Modulus); var webKey = new Models.JsonWebKey { kty = "RSA", use = "sig", kid = rsaKey.KeyId, e = exponent, n = modulus, alg = key.SigningAlgorithm }; webKeys.Add(webKey); } else if (key.Key is ECDsaSecurityKey ecdsaKey) { var parameters = ecdsaKey.ECDsa.ExportParameters(false); var x = Base64Url.Encode(parameters.Q.X); var y = Base64Url.Encode(parameters.Q.Y); var ecdsaJsonWebKey = new Models.JsonWebKey { kty = "EC", use = "sig", kid = ecdsaKey.KeyId, x = x, y = y, crv = CryptoHelper.GetCrvValueFromCurve(parameters.Curve), alg = key.SigningAlgorithm }; webKeys.Add(ecdsaJsonWebKey); } else if (key.Key is JsonWebKey jsonWebKey) { var webKey = new Models.JsonWebKey { kty = jsonWebKey.Kty, use = jsonWebKey.Use ?? "sig", kid = jsonWebKey.Kid, x5t = jsonWebKey.X5t, e = jsonWebKey.E, n = jsonWebKey.N, x5c = jsonWebKey.X5c?.Count == 0 ? null : jsonWebKey.X5c.ToArray(), alg = jsonWebKey.Alg, crv = jsonWebKey.Crv, x = jsonWebKey.X, y = jsonWebKey.Y }; webKeys.Add(webKey); } } return webKeys; } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Default/IntrospectionResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// The introspection response generator /// /// public class IntrospectionResponseGenerator : IIntrospectionResponseGenerator { /// /// Gets the events. /// /// /// The events. /// protected readonly IEventService Events; /// /// The logger /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The events. /// The logger. public IntrospectionResponseGenerator(IEventService events, ILogger logger) { Events = events; Logger = logger; } /// /// Processes the response. /// /// The validation result. /// public virtual async Task> ProcessAsync(IntrospectionRequestValidationResult validationResult) { Logger.LogTrace("Creating introspection response"); // standard response var response = new Dictionary { { "active", false } }; // token is invalid if (validationResult.IsActive == false) { Logger.LogDebug("Creating introspection response for inactive token."); await Events.RaiseAsync(new TokenIntrospectionSuccessEvent(validationResult)); return response; } // expected scope not present if (await AreExpectedScopesPresentAsync(validationResult) == false) { return response; } Logger.LogDebug("Creating introspection response for active token."); // get all claims (without scopes) response = validationResult.Claims.Where(c => c.Type != JwtClaimTypes.Scope).ToClaimsDictionary(); // add active flag response.Add("active", true); // calculate scopes the caller is allowed to see var allowedScopes = validationResult.Api.Scopes; var scopes = validationResult.Claims.Where(c => c.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes = scopes.Where(x => allowedScopes.Contains(x)); response.Add("scope", scopes.ToSpaceSeparatedString()); await Events.RaiseAsync(new TokenIntrospectionSuccessEvent(validationResult)); return response; } /// /// Checks if the API resource is allowed to introspect the scopes. /// /// The validation result. /// protected virtual async Task AreExpectedScopesPresentAsync(IntrospectionRequestValidationResult validationResult) { var apiScopes = validationResult.Api.Scopes; var tokenScopes = validationResult.Claims.Where(c => c.Type == JwtClaimTypes.Scope); var tokenScopesThatMatchApi = tokenScopes.Where(c => apiScopes.Contains(c.Value)); var result = false; if (tokenScopesThatMatchApi.Any()) { // at least one of the scopes the API supports is in the token result = true; } else { // no scopes for this API are found in the token Logger.LogError("Expected scope {scopes} is missing in token", apiScopes); await Events.RaiseAsync(new TokenIntrospectionFailureEvent(validationResult.Api.Name, "Expected scopes are missing", validationResult.Token, apiScopes, tokenScopes.Select(s => s.Value))); } return result; } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Default/TokenResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// The default token response generator /// /// public class TokenResponseGenerator : ITokenResponseGenerator { /// /// The logger /// protected readonly ILogger Logger; /// /// The token service /// protected readonly ITokenService TokenService; /// /// The refresh token service /// protected readonly IRefreshTokenService RefreshTokenService; /// /// The scope parser /// public IScopeParser ScopeParser { get; } /// /// The resource store /// protected readonly IResourceStore Resources; /// /// The clients store /// protected readonly IClientStore Clients; /// /// The clock /// protected readonly ISystemClock Clock; /// /// Initializes a new instance of the class. /// /// The clock. /// The token service. /// The refresh token service. /// The scope parser. /// The resources. /// The clients. /// The logger. public TokenResponseGenerator(ISystemClock clock, ITokenService tokenService, IRefreshTokenService refreshTokenService, IScopeParser scopeParser, IResourceStore resources, IClientStore clients, ILogger logger) { Clock = clock; TokenService = tokenService; RefreshTokenService = refreshTokenService; ScopeParser = scopeParser; Resources = resources; Clients = clients; Logger = logger; } /// /// Processes the response. /// /// The request. /// public virtual async Task ProcessAsync(TokenRequestValidationResult request) { switch (request.ValidatedRequest.GrantType) { case OidcConstants.GrantTypes.ClientCredentials: return await ProcessClientCredentialsRequestAsync(request); case OidcConstants.GrantTypes.Password: return await ProcessPasswordRequestAsync(request); case OidcConstants.GrantTypes.AuthorizationCode: return await ProcessAuthorizationCodeRequestAsync(request); case OidcConstants.GrantTypes.RefreshToken: return await ProcessRefreshTokenRequestAsync(request); case OidcConstants.GrantTypes.DeviceCode: return await ProcessDeviceCodeRequestAsync(request); default: return await ProcessExtensionGrantRequestAsync(request); } } /// /// Creates the response for an client credentials request. /// /// The request. /// protected virtual Task ProcessClientCredentialsRequestAsync(TokenRequestValidationResult request) { Logger.LogTrace("Creating response for client credentials request"); return ProcessTokenRequestAsync(request); } /// /// Creates the response for a password request. /// /// The request. /// protected virtual Task ProcessPasswordRequestAsync(TokenRequestValidationResult request) { Logger.LogTrace("Creating response for password request"); return ProcessTokenRequestAsync(request); } /// /// Creates the response for an authorization code request. /// /// The request. /// /// Client does not exist anymore. protected virtual async Task ProcessAuthorizationCodeRequestAsync(TokenRequestValidationResult request) { Logger.LogTrace("Creating response for authorization code request"); ////////////////////////// // access token ///////////////////////// var (accessToken, refreshToken) = await CreateAccessTokenAsync(request.ValidatedRequest); var response = new TokenResponse { AccessToken = accessToken, AccessTokenLifetime = request.ValidatedRequest.AccessTokenLifetime, Custom = request.CustomResponse, Scope = request.ValidatedRequest.AuthorizationCode.RequestedScopes.ToSpaceSeparatedString() }; ////////////////////////// // refresh token ///////////////////////// if (refreshToken.IsPresent()) { response.RefreshToken = refreshToken; } ////////////////////////// // id token ///////////////////////// if (request.ValidatedRequest.AuthorizationCode.IsOpenId) { // load the client that belongs to the authorization code Client client = null; if (request.ValidatedRequest.AuthorizationCode.ClientId != null) { client = await Clients.FindEnabledClientByIdAsync(request.ValidatedRequest.AuthorizationCode.ClientId); } if (client == null) { throw new InvalidOperationException("Client does not exist anymore."); } var parsedScopesResult = ScopeParser.ParseScopeValues(request.ValidatedRequest.AuthorizationCode.RequestedScopes); var validatedResources = await Resources.CreateResourceValidationResult(parsedScopesResult); var tokenRequest = new TokenCreationRequest { Subject = request.ValidatedRequest.AuthorizationCode.Subject, ValidatedResources = validatedResources, Nonce = request.ValidatedRequest.AuthorizationCode.Nonce, AccessTokenToHash = response.AccessToken, StateHash = request.ValidatedRequest.AuthorizationCode.StateHash, ValidatedRequest = request.ValidatedRequest }; var idToken = await TokenService.CreateIdentityTokenAsync(tokenRequest); var jwt = await TokenService.CreateSecurityTokenAsync(idToken); response.IdentityToken = jwt; } return response; } /// /// Creates the response for a refresh token request. /// /// The request. /// protected virtual async Task ProcessRefreshTokenRequestAsync(TokenRequestValidationResult request) { Logger.LogTrace("Creating response for refresh token request"); var oldAccessToken = request.ValidatedRequest.RefreshToken.AccessToken; string accessTokenString; if (request.ValidatedRequest.Client.UpdateAccessTokenClaimsOnRefresh) { var subject = request.ValidatedRequest.RefreshToken.Subject; // todo: do we want to just parse here and build up validated result // or do we want to fully re-run validation here. var parsedScopesResult = ScopeParser.ParseScopeValues(oldAccessToken.Scopes); var validatedResources = await Resources.CreateResourceValidationResult(parsedScopesResult); var creationRequest = new TokenCreationRequest { Subject = subject, Description = request.ValidatedRequest.RefreshToken.Description, ValidatedRequest = request.ValidatedRequest, ValidatedResources = validatedResources }; var newAccessToken = await TokenService.CreateAccessTokenAsync(creationRequest); accessTokenString = await TokenService.CreateSecurityTokenAsync(newAccessToken); } else { oldAccessToken.CreationTime = Clock.UtcNow.UtcDateTime; oldAccessToken.Lifetime = request.ValidatedRequest.AccessTokenLifetime; accessTokenString = await TokenService.CreateSecurityTokenAsync(oldAccessToken); } var handle = await RefreshTokenService.UpdateRefreshTokenAsync(request.ValidatedRequest.RefreshTokenHandle, request.ValidatedRequest.RefreshToken, request.ValidatedRequest.Client); return new TokenResponse { IdentityToken = await CreateIdTokenFromRefreshTokenRequestAsync(request.ValidatedRequest, accessTokenString), AccessToken = accessTokenString, AccessTokenLifetime = request.ValidatedRequest.AccessTokenLifetime, RefreshToken = handle, Custom = request.CustomResponse, Scope = request.ValidatedRequest.RefreshToken.Scopes.ToSpaceSeparatedString() }; } /// /// Processes the response for device code grant request. /// /// The request. /// protected virtual async Task ProcessDeviceCodeRequestAsync(TokenRequestValidationResult request) { Logger.LogTrace("Creating response for device code request"); ////////////////////////// // access token ///////////////////////// var (accessToken, refreshToken) = await CreateAccessTokenAsync(request.ValidatedRequest); var response = new TokenResponse { AccessToken = accessToken, AccessTokenLifetime = request.ValidatedRequest.AccessTokenLifetime, Custom = request.CustomResponse, Scope = request.ValidatedRequest.DeviceCode.AuthorizedScopes.ToSpaceSeparatedString() }; ////////////////////////// // refresh token ///////////////////////// if (refreshToken.IsPresent()) { response.RefreshToken = refreshToken; } ////////////////////////// // id token ///////////////////////// if (request.ValidatedRequest.DeviceCode.IsOpenId) { // load the client that belongs to the device code Client client = null; if (request.ValidatedRequest.DeviceCode.ClientId != null) { client = await Clients.FindEnabledClientByIdAsync(request.ValidatedRequest.DeviceCode.ClientId); } if (client == null) { throw new InvalidOperationException("Client does not exist anymore."); } var parsedScopesResult = ScopeParser.ParseScopeValues(request.ValidatedRequest.DeviceCode.AuthorizedScopes); var validatedResources = await Resources.CreateResourceValidationResult(parsedScopesResult); var tokenRequest = new TokenCreationRequest { Subject = request.ValidatedRequest.DeviceCode.Subject, ValidatedResources = validatedResources, AccessTokenToHash = response.AccessToken, ValidatedRequest = request.ValidatedRequest }; var idToken = await TokenService.CreateIdentityTokenAsync(tokenRequest); var jwt = await TokenService.CreateSecurityTokenAsync(idToken); response.IdentityToken = jwt; } return response; } /// /// Creates the response for an extension grant request. /// /// The request. /// protected virtual Task ProcessExtensionGrantRequestAsync(TokenRequestValidationResult request) { Logger.LogTrace("Creating response for extension grant request"); return ProcessTokenRequestAsync(request); } /// /// Creates the response for a token request. /// /// The validation result. /// protected virtual async Task ProcessTokenRequestAsync(TokenRequestValidationResult validationResult) { (var accessToken, var refreshToken) = await CreateAccessTokenAsync(validationResult.ValidatedRequest); var response = new TokenResponse { AccessToken = accessToken, AccessTokenLifetime = validationResult.ValidatedRequest.AccessTokenLifetime, Custom = validationResult.CustomResponse, Scope = validationResult.ValidatedRequest.ValidatedResources.RawScopeValues.ToSpaceSeparatedString() }; if (refreshToken.IsPresent()) { response.RefreshToken = refreshToken; } return response; } /// /// Creates the access/refresh token. /// /// The request. /// /// Client does not exist anymore. protected virtual async Task<(string accessToken, string refreshToken)> CreateAccessTokenAsync(ValidatedTokenRequest request) { TokenCreationRequest tokenRequest; bool createRefreshToken; if (request.AuthorizationCode != null) { createRefreshToken = request.AuthorizationCode.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OfflineAccess); // load the client that belongs to the authorization code Client client = null; if (request.AuthorizationCode.ClientId != null) { client = await Clients.FindEnabledClientByIdAsync(request.AuthorizationCode.ClientId); } if (client == null) { throw new InvalidOperationException("Client does not exist anymore."); } var parsedScopesResult = ScopeParser.ParseScopeValues(request.AuthorizationCode.RequestedScopes); var validatedResources = await Resources.CreateResourceValidationResult(parsedScopesResult); tokenRequest = new TokenCreationRequest { Subject = request.AuthorizationCode.Subject, Description = request.AuthorizationCode.Description, ValidatedResources = validatedResources, ValidatedRequest = request }; } else if (request.DeviceCode != null) { createRefreshToken = request.DeviceCode.AuthorizedScopes.Contains(IdentityServerConstants.StandardScopes.OfflineAccess); Client client = null; if (request.DeviceCode.ClientId != null) { client = await Clients.FindEnabledClientByIdAsync(request.DeviceCode.ClientId); } if (client == null) { throw new InvalidOperationException("Client does not exist anymore."); } var parsedScopesResult = ScopeParser.ParseScopeValues(request.DeviceCode.AuthorizedScopes); var validatedResources = await Resources.CreateResourceValidationResult(parsedScopesResult); tokenRequest = new TokenCreationRequest { Subject = request.DeviceCode.Subject, Description = request.DeviceCode.Description, ValidatedResources = validatedResources, ValidatedRequest = request }; } else { createRefreshToken = request.ValidatedResources.Resources.OfflineAccess; tokenRequest = new TokenCreationRequest { Subject = request.Subject, ValidatedResources = request.ValidatedResources, ValidatedRequest = request }; } var at = await TokenService.CreateAccessTokenAsync(tokenRequest); var accessToken = await TokenService.CreateSecurityTokenAsync(at); if (createRefreshToken) { var refreshToken = await RefreshTokenService.CreateRefreshTokenAsync(tokenRequest.Subject, at, request.Client); return (accessToken, refreshToken); } return (accessToken, null); } /// /// Creates an id_token for a refresh token request if identity resources have been requested. /// /// The request. /// The new access token. /// protected virtual async Task CreateIdTokenFromRefreshTokenRequestAsync(ValidatedTokenRequest request, string newAccessToken) { // todo: can we just check for "openid" scope? //var identityResources = await Resources.FindEnabledIdentityResourcesByScopeAsync(request.RefreshToken.Scopes); //if (identityResources.Any()) if (request.RefreshToken.Scopes.Contains(OidcConstants.StandardScopes.OpenId)) { var oldAccessToken = request.RefreshToken.AccessToken; var parsedScopesResult = ScopeParser.ParseScopeValues(oldAccessToken.Scopes); var validatedResources = await Resources.CreateResourceValidationResult(parsedScopesResult); var tokenRequest = new TokenCreationRequest { Subject = request.RefreshToken.Subject, ValidatedResources = validatedResources, ValidatedRequest = request, AccessTokenToHash = newAccessToken }; var idToken = await TokenService.CreateIdentityTokenAsync(tokenRequest); return await TokenService.CreateSecurityTokenAsync(idToken); } return null; } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Default/TokenRevocationResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Default revocation response generator /// /// public class TokenRevocationResponseGenerator : ITokenRevocationResponseGenerator { /// /// Gets the reference token store. /// /// /// The reference token store. /// protected readonly IReferenceTokenStore ReferenceTokenStore; /// /// Gets the refresh token store. /// /// /// The refresh token store. /// protected readonly IRefreshTokenStore RefreshTokenStore; /// /// Gets the logger. /// /// /// The logger. /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The reference token store. /// The refresh token store. /// The logger. public TokenRevocationResponseGenerator(IReferenceTokenStore referenceTokenStore, IRefreshTokenStore refreshTokenStore, ILogger logger) { ReferenceTokenStore = referenceTokenStore; RefreshTokenStore = refreshTokenStore; Logger = logger; } /// /// Creates the revocation endpoint response and processes the revocation request. /// /// The userinfo request validation result. /// public virtual async Task ProcessAsync(TokenRevocationRequestValidationResult validationResult) { var response = new TokenRevocationResponse { Success = false, TokenType = validationResult.TokenTypeHint }; // revoke tokens if (validationResult.TokenTypeHint == Constants.TokenTypeHints.AccessToken) { Logger.LogTrace("Hint was for access token"); response.Success = await RevokeAccessTokenAsync(validationResult); } else if (validationResult.TokenTypeHint == Constants.TokenTypeHints.RefreshToken) { Logger.LogTrace("Hint was for refresh token"); response.Success = await RevokeRefreshTokenAsync(validationResult); } else { Logger.LogTrace("No hint for token type"); response.Success = await RevokeAccessTokenAsync(validationResult); if (!response.Success) { response.Success = await RevokeRefreshTokenAsync(validationResult); response.TokenType = Constants.TokenTypeHints.RefreshToken; } else { response.TokenType = Constants.TokenTypeHints.AccessToken; } } return response; } /// /// Revoke access token only if it belongs to client doing the request. /// protected virtual async Task RevokeAccessTokenAsync(TokenRevocationRequestValidationResult validationResult) { var token = await ReferenceTokenStore.GetReferenceTokenAsync(validationResult.Token); if (token != null) { if (token.ClientId == validationResult.Client.ClientId) { Logger.LogDebug("Access token revoked"); await ReferenceTokenStore.RemoveReferenceTokenAsync(validationResult.Token); } else { Logger.LogWarning("Client {clientId} denied from revoking access token belonging to Client {tokenClientId}", validationResult.Client.ClientId, token.ClientId); } return true; } return false; } /// /// Revoke refresh token only if it belongs to client doing the request /// protected virtual async Task RevokeRefreshTokenAsync(TokenRevocationRequestValidationResult validationResult) { var token = await RefreshTokenStore.GetRefreshTokenAsync(validationResult.Token); if (token != null) { if (token.ClientId == validationResult.Client.ClientId) { Logger.LogDebug("Refresh token revoked"); await RefreshTokenStore.RemoveRefreshTokenAsync(validationResult.Token); await ReferenceTokenStore.RemoveReferenceTokensAsync(token.SubjectId, token.ClientId); } else { Logger.LogWarning("Client {clientId} denied from revoking a refresh token belonging to Client {tokenClientId}", validationResult.Client.ClientId, token.ClientId); } return true; } return false; } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Default/UserInfoResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// The userinfo response generator /// /// public class UserInfoResponseGenerator : IUserInfoResponseGenerator { /// /// The logger /// protected readonly ILogger Logger; /// /// The profile service /// protected readonly IProfileService Profile; /// /// The resource store /// protected readonly IResourceStore Resources; /// /// Initializes a new instance of the class. /// /// The profile. /// The resource store. /// The logger. public UserInfoResponseGenerator(IProfileService profile, IResourceStore resourceStore, ILogger logger) { Profile = profile; Resources = resourceStore; Logger = logger; } /// /// Creates the response. /// /// The userinfo request validation result. /// /// Profile service returned incorrect subject value public virtual async Task> ProcessAsync(UserInfoRequestValidationResult validationResult) { Logger.LogDebug("Creating userinfo response"); // extract scopes and turn into requested claim types var scopes = validationResult.TokenValidationResult.Claims.Where(c => c.Type == JwtClaimTypes.Scope).Select(c => c.Value); var validatedResources = await GetRequestedResourcesAsync(scopes); var requestedClaimTypes = await GetRequestedClaimTypesAsync(validatedResources); Logger.LogDebug("Requested claim types: {claimTypes}", requestedClaimTypes.ToSpaceSeparatedString()); // call profile service var context = new ProfileDataRequestContext( validationResult.Subject, validationResult.TokenValidationResult.Client, IdentityServerConstants.ProfileDataCallers.UserInfoEndpoint, requestedClaimTypes); context.RequestedResources = validatedResources; await Profile.GetProfileDataAsync(context); var profileClaims = context.IssuedClaims; // construct outgoing claims var outgoingClaims = new List(); if (profileClaims == null) { Logger.LogInformation("Profile service returned no claims (null)"); } else { outgoingClaims.AddRange(profileClaims); Logger.LogInformation("Profile service returned the following claim types: {types}", profileClaims.Select(c => c.Type).ToSpaceSeparatedString()); } var subClaim = outgoingClaims.SingleOrDefault(x => x.Type == JwtClaimTypes.Subject); if (subClaim == null) { outgoingClaims.Add(new Claim(JwtClaimTypes.Subject, validationResult.Subject.GetSubjectId())); } else if (subClaim.Value != validationResult.Subject.GetSubjectId()) { Logger.LogError("Profile service returned incorrect subject value: {sub}", subClaim); throw new InvalidOperationException("Profile service returned incorrect subject value"); } return outgoingClaims.ToClaimsDictionary(); } /// /// Gets the identity resources from the scopes. /// /// /// protected internal virtual async Task GetRequestedResourcesAsync(IEnumerable scopes) { if (scopes == null || !scopes.Any()) { return null; } var scopeString = string.Join(" ", scopes); Logger.LogDebug("Scopes in access token: {scopes}", scopeString); // if we ever parameterize identity scopes, then we would need to invoke the resource validator's parse API here var identityResources = await Resources.FindEnabledIdentityResourcesByScopeAsync(scopes); var resources = new Resources(identityResources, Enumerable.Empty(), Enumerable.Empty()); var result = new ResourceValidationResult(resources); return result; } /// /// Gets the requested claim types. /// /// /// protected internal virtual Task> GetRequestedClaimTypesAsync(ResourceValidationResult resourceValidationResult) { IEnumerable result = null; if (resourceValidationResult == null) { result = Enumerable.Empty(); } else { var identityResources = resourceValidationResult.Resources.IdentityResources; result = identityResources.SelectMany(x => x.UserClaims).Distinct(); } return Task.FromResult(result); } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/IAuthorizeInteractionResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Interface for determining if user must login or consent when making requests to the authorization endpoint. /// public interface IAuthorizeInteractionResponseGenerator { /// /// Processes the interaction logic. /// /// The request. /// The consent. /// Task ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/IAuthorizeResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Interface for the authorize response generator /// public interface IAuthorizeResponseGenerator { /// /// Creates the response /// /// The request. /// Task CreateResponseAsync(ValidatedAuthorizeRequest request); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/IDeviceAuthorizationResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Interface for the device authorization response generator /// public interface IDeviceAuthorizationResponseGenerator { /// /// Processes the response. /// /// The validation result. /// The base URL. /// Task ProcessAsync(DeviceAuthorizationRequestValidationResult validationResult, string baseUrl); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/IDiscoveryResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Interface for discovery endpoint response generator /// public interface IDiscoveryResponseGenerator { /// /// Creates the discovery document. /// /// The base URL. /// The issuer URI. Task> CreateDiscoveryDocumentAsync(string baseUrl, string issuerUri); /// /// Creates the JWK document. /// Task> CreateJwkDocumentAsync(); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/IIntrospectionResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Interface for introspection response generator /// public interface IIntrospectionResponseGenerator { /// /// Processes the response. /// /// The validation result. /// Task> ProcessAsync(IntrospectionRequestValidationResult validationResult); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/ITokenResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Interface the token response generator /// public interface ITokenResponseGenerator { /// /// Processes the response. /// /// The validation result. /// Task ProcessAsync(TokenRequestValidationResult validationResult); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/ITokenRevocationResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Interface for the userinfo response generator /// public interface ITokenRevocationResponseGenerator { /// /// Creates the revocation endpoint response and processes the revocation request. /// /// The userinfo request validation result. /// Task ProcessAsync(TokenRevocationRequestValidationResult validationResult); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/IUserInfoResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Interface for the userinfo response generator /// public interface IUserInfoResponseGenerator { /// /// Creates the response. /// /// The userinfo request validation result. /// Task> ProcessAsync(UserInfoRequestValidationResult validationResult); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Models/AuthorizeResponse.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.ResponseHandling; public class AuthorizeResponse { public ValidatedAuthorizeRequest Request { get; set; } public string RedirectUri => Request?.RedirectUri; public string State => Request?.State; public string Scope => Request?.ValidatedResources?.RawScopeValues.ToSpaceSeparatedString(); public string IdentityToken { get; set; } public string AccessToken { get; set; } public int AccessTokenLifetime { get; set; } public string Code { get; set; } public string SessionState { get; set; } public string Error { get; set; } public string ErrorDescription { get; set; } public bool IsError => Error.IsPresent(); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Models/DeviceAuthorizationResponse.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.ResponseHandling; public class DeviceAuthorizationResponse { public string DeviceCode { get; set; } public string UserCode { get; set; } public string VerificationUri { get; set; } public string VerificationUriComplete { get; set; } public int DeviceCodeLifetime { get; set; } public int Interval { get; set; } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Models/InteractionResponse.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Indicates interaction outcome for user on authorization endpoint. /// public class InteractionResponse { /// /// Gets or sets a value indicating whether the user must login. /// /// /// true if this instance is login; otherwise, false. /// public bool IsLogin { get; set; } /// /// Gets or sets a value indicating whether the user must consent. /// /// /// true if this instance is consent; otherwise, false. /// public bool IsConsent { get; set; } /// /// Gets a value indicating whether the result is an error. /// /// /// true if this instance is error; otherwise, false. /// public bool IsError => Error != null; /// /// Gets or sets the error. /// /// /// The error. /// public string Error { get; set; } /// /// Gets or sets the error description. /// /// /// The error description. /// public string ErrorDescription { get; set; } /// /// Gets a value indicating whether the user must be redirected to a custom page. /// /// /// true if this instance is redirect; otherwise, false. /// public bool IsRedirect => RedirectUrl.IsPresent(); /// /// Gets or sets the URL for the custom page. /// /// /// The redirect URL. /// public string RedirectUrl { get; set; } } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Models/TokenErrorResponse.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Models a token error response /// public class TokenErrorResponse { /// /// Gets or sets the error. /// /// /// The error. /// public string Error { get; set; } = OidcConstants.TokenErrors.InvalidRequest; /// /// Gets or sets the error description. /// /// /// The error description. /// public string ErrorDescription { get; set; } /// /// Gets or sets the custom entries. /// /// /// The custom. /// public Dictionary Custom { get; set; } = new Dictionary(); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Models/TokenResponse.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Models a token response /// public class TokenResponse { /// /// Gets or sets the identity token. /// /// /// The identity token. /// public string IdentityToken { get; set; } /// /// Gets or sets the access token. /// /// /// The access token. /// public string AccessToken { get; set; } /// /// Gets or sets the access token lifetime. /// /// /// The access token lifetime. /// public int AccessTokenLifetime { get; set; } /// /// Gets or sets the refresh token. /// /// /// The refresh token. /// public string RefreshToken { get; set; } /// /// Gets or sets the scope. /// /// /// The scope. /// public string Scope { get; set; } /// /// Gets or sets the custom entries. /// /// /// The custom entries. /// public Dictionary Custom { get; set; } = new Dictionary(); } ================================================ FILE: src/IdentityServer8/src/ResponseHandling/Models/TokenRevocationResponse.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.ResponseHandling; /// /// Models a token revocation response /// public class TokenRevocationResponse { /// /// Gets or sets a value indicating whether the token revocation was successful. /// /// /// true if success; otherwise, false. /// public bool Success { get; set; } /// /// Gets or sets the type of the token that was revoked. /// /// /// The type of the token. /// public string TokenType { get; set; } /// /// Gets or sets an error (if present). /// /// /// The error. /// public string Error { get; set; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/BackChannelLogoutHttpClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Models making HTTP requests for back-channel logout notification. /// public class DefaultBackChannelLogoutHttpClient : IBackChannelLogoutHttpClient { private readonly HttpClient _client; private readonly ILogger _logger; /// /// Constructor for BackChannelLogoutHttpClient. /// /// /// public DefaultBackChannelLogoutHttpClient(HttpClient client, ILoggerFactory loggerFactory) { _client = client; _logger = loggerFactory.CreateLogger(); } /// /// Posts the payload to the url. /// /// /// /// public async Task PostAsync(string url, Dictionary payload) { try { var response = await _client.PostAsync(url, new FormUrlEncodedContent(payload)); if (response.IsSuccessStatusCode) { _logger.LogDebug("Response from back-channel logout endpoint: {url} status code: {status}", url, (int)response.StatusCode); } else { _logger.LogWarning("Response from back-channel logout endpoint: {url} status code: {status}", url, (int)response.StatusCode); } } catch (Exception ex) { _logger.LogError(ex, "Exception invoking back-channel logout for url: {url}", url); } } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultBackChannelLogoutService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default back-channel logout notification implementation. /// public class DefaultBackChannelLogoutService : IBackChannelLogoutService { /// /// Default value for the back-channel JWT lifetime. /// protected const int DefaultLogoutTokenLifetime = 5 * 60; /// /// The system clock; /// protected ISystemClock Clock { get; } /// /// The IdentityServerTools used to create and the JWT. /// protected IdentityServerTools Tools { get; } /// /// The ILogoutNotificationService to build the back channel logout requests. /// public ILogoutNotificationService LogoutNotificationService { get; } /// /// HttpClient to make the outbound HTTP calls. /// protected IBackChannelLogoutHttpClient HttpClient { get; } /// /// The logger. /// protected ILogger Logger { get; } /// /// Constructor. /// /// /// /// /// /// public DefaultBackChannelLogoutService( ISystemClock clock, IdentityServerTools tools, ILogoutNotificationService logoutNotificationService, IBackChannelLogoutHttpClient backChannelLogoutHttpClient, ILogger logger) { Clock = clock; Tools = tools; LogoutNotificationService = logoutNotificationService; HttpClient = backChannelLogoutHttpClient; Logger = logger; } /// public virtual async Task SendLogoutNotificationsAsync(LogoutNotificationContext context) { var backChannelRequests = await LogoutNotificationService.GetBackChannelLogoutNotificationsAsync(context); if (backChannelRequests.Any()) { await SendLogoutNotificationsAsync(backChannelRequests); } } /// /// Sends the logout notifications for the collection of clients. /// /// /// protected virtual Task SendLogoutNotificationsAsync(IEnumerable requests) { requests = requests ?? Enumerable.Empty(); var tasks = requests.Select(SendLogoutNotificationAsync).ToArray(); return Task.WhenAll(tasks); } /// /// Performs the back-channel logout for a single client. /// /// protected virtual async Task SendLogoutNotificationAsync(BackChannelLogoutRequest request) { var data = await CreateFormPostPayloadAsync(request); await PostLogoutJwt(request, data); } /// /// Performs the HTTP POST of the logout payload to the client. /// /// /// /// protected virtual Task PostLogoutJwt(BackChannelLogoutRequest client, Dictionary data) { return HttpClient.PostAsync(client.LogoutUri, data); } /// /// Creates the form-url-encoded payload (as a dictionary) to send to the client. /// /// /// protected async Task> CreateFormPostPayloadAsync(BackChannelLogoutRequest request) { var token = await CreateTokenAsync(request); var data = new Dictionary { { OidcConstants.BackChannelLogoutRequest.LogoutToken, token } }; return data; } /// /// Creates the JWT used for the back-channel logout notification. /// /// /// The token. protected virtual async Task CreateTokenAsync(BackChannelLogoutRequest request) { var claims = await CreateClaimsForTokenAsync(request); if (claims.Any(x => x.Type == JwtClaimTypes.Nonce)) { throw new InvalidOperationException("nonce claim is not allowed in the back-channel signout token."); } return await Tools.IssueJwtAsync(DefaultLogoutTokenLifetime, claims); } /// /// Create the claims to be used in the back-channel logout token. /// /// /// The claims to include in the token. protected Task> CreateClaimsForTokenAsync(BackChannelLogoutRequest request) { if (request.SessionIdRequired && request.SessionId == null) { throw new ArgumentException("Client requires SessionId", nameof(request.SessionId)); } var json = "{\"" + OidcConstants.Events.BackChannelLogout + "\":{} }"; var claims = new List { new Claim(JwtClaimTypes.Subject, request.SubjectId), new Claim(JwtClaimTypes.Audience, request.ClientId), new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex)), new Claim(JwtClaimTypes.Events, json, IdentityServerConstants.ClaimValueTypes.Json) }; if (request.SessionId != null) { claims.Add(new Claim(JwtClaimTypes.SessionId, request.SessionId)); } return Task.FromResult(claims.AsEnumerable()); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultCache.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// IMemoryCache-based implementation of the cache /// /// /// public class DefaultCache : ICache where T : class { private const string KeySeparator = ":"; private readonly IMemoryCache _cache; /// /// Initializes a new instance of the class. /// /// The cache. public DefaultCache(IMemoryCache cache) { _cache = cache; } private string GetKey(string key) { return typeof(T).FullName + KeySeparator + key; } /// /// Gets the cached data based upon a key index. /// /// The key. /// /// The cached item, or null if no item matches the key. /// public Task GetAsync(string key) { key = GetKey(key); var item = _cache.Get(key); return Task.FromResult(item); } /// /// Caches the data based upon a key /// /// The key. /// The item. /// The expiration. /// public Task SetAsync(string key, T item, TimeSpan expiration) { key = GetKey(key); _cache.Set(key, item, expiration); return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultClaimsService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default claims provider implementation /// public class DefaultClaimsService : IClaimsService { /// /// The logger /// protected readonly ILogger Logger; /// /// The user service /// protected readonly IProfileService Profile; /// /// Initializes a new instance of the class. /// /// The profile service /// The logger public DefaultClaimsService(IProfileService profile, ILogger logger) { Logger = logger; Profile = profile; } /// /// Returns claims for an identity token /// /// The subject /// The requested resources /// Specifies if all claims should be included in the token, or if the userinfo endpoint can be used to retrieve them /// The raw request /// /// Claims for the identity token /// public virtual async Task> GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resources, bool includeAllIdentityClaims, ValidatedRequest request) { Logger.LogDebug("Getting claims for identity token for subject: {subject} and client: {clientId}", subject.GetSubjectId(), request.Client.ClientId); var outputClaims = new List(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // fetch all identity claims that need to go into the id token if (includeAllIdentityClaims || request.Client.AlwaysIncludeUserClaimsInIdToken) { var additionalClaimTypes = new List(); foreach (var identityResource in resources.Resources.IdentityResources) { foreach (var userClaim in identityResource.UserClaims) { additionalClaimTypes.Add(userClaim); } } // filter so we don't ask for claim types that we will eventually filter out additionalClaimTypes = FilterRequestedClaimTypes(additionalClaimTypes).ToList(); var context = new ProfileDataRequestContext( subject, request.Client, IdentityServerConstants.ProfileDataCallers.ClaimsProviderIdentityToken, additionalClaimTypes) { RequestedResources = resources, ValidatedRequest = request }; await Profile.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } else { Logger.LogDebug("In addition to an id_token, an access_token was requested. No claims other than sub are included in the id_token. To obtain more user claims, either use the user info endpoint or set AlwaysIncludeUserClaimsInIdToken on the client configuration."); } return outputClaims; } /// /// Returns claims for an access token. /// /// The subject. /// The validated resource result /// The raw request. /// /// Claims for the access token /// public virtual async Task> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resourceResult, ValidatedRequest request) { Logger.LogDebug("Getting claims for access token for client: {clientId}", request.Client.ClientId); var outputClaims = new List { new Claim(JwtClaimTypes.ClientId, request.ClientId) }; // log if client ID is overwritten if (!string.Equals(request.ClientId, request.Client.ClientId)) { Logger.LogDebug("Client {clientId} is impersonating {impersonatedClientId}", request.Client.ClientId, request.ClientId); } // check for client claims if (request.ClientClaims != null && request.ClientClaims.Any()) { if (subject == null || request.Client.AlwaysSendClientClaims) { foreach (var claim in request.ClientClaims) { var claimType = claim.Type; if (request.Client.ClientClaimsPrefix.IsPresent()) { claimType = request.Client.ClientClaimsPrefix + claimType; } outputClaims.Add(new Claim(claimType, claim.Value, claim.ValueType)); } } } // add scopes (filter offline_access) // we use the ScopeValues collection rather than the Resources.Scopes because we support dynamic scope values // from the request, so this issues those in the token. foreach (var scope in resourceResult.RawScopeValues.Where(x => x != IdentityServerConstants.StandardScopes.OfflineAccess)) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, scope)); } // a user is involved if (subject != null) { if (resourceResult.Resources.OfflineAccess) { outputClaims.Add(new Claim(JwtClaimTypes.Scope, IdentityServerConstants.StandardScopes.OfflineAccess)); } Logger.LogDebug("Getting claims for access token for subject: {subject}", subject.GetSubjectId()); outputClaims.AddRange(GetStandardSubjectClaims(subject)); outputClaims.AddRange(GetOptionalClaims(subject)); // fetch all resource claims that need to go into the access token var additionalClaimTypes = new List(); foreach (var api in resourceResult.Resources.ApiResources) { // add claims configured on api resource if (api.UserClaims != null) { foreach (var claim in api.UserClaims) { additionalClaimTypes.Add(claim); } } } foreach(var scope in resourceResult.Resources.ApiScopes) { // add claims configured on scopes if (scope.UserClaims != null) { foreach (var claim in scope.UserClaims) { additionalClaimTypes.Add(claim); } } } // filter so we don't ask for claim types that we will eventually filter out additionalClaimTypes = FilterRequestedClaimTypes(additionalClaimTypes).ToList(); var context = new ProfileDataRequestContext( subject, request.Client, IdentityServerConstants.ProfileDataCallers.ClaimsProviderAccessToken, additionalClaimTypes.Distinct()) { RequestedResources = resourceResult, ValidatedRequest = request }; await Profile.GetProfileDataAsync(context); var claims = FilterProtocolClaims(context.IssuedClaims); if (claims != null) { outputClaims.AddRange(claims); } } return outputClaims; } /// /// Gets the standard subject claims. /// /// The subject. /// A list of standard claims protected virtual IEnumerable GetStandardSubjectClaims(ClaimsPrincipal subject) { var claims = new List { new Claim(JwtClaimTypes.Subject, subject.GetSubjectId()), new Claim(JwtClaimTypes.AuthenticationTime, subject.GetAuthenticationTimeEpoch().ToString(), ClaimValueTypes.Integer64), new Claim(JwtClaimTypes.IdentityProvider, subject.GetIdentityProvider()) }; claims.AddRange(subject.GetAuthenticationMethods()); return claims; } /// /// Gets additional (and optional) claims from the cookie or incoming subject. /// /// The subject. /// Additional claims protected virtual IEnumerable GetOptionalClaims(ClaimsPrincipal subject) { var claims = new List(); var acr = subject.FindFirst(JwtClaimTypes.AuthenticationContextClassReference); if (acr != null) claims.Add(acr); return claims; } /// /// Filters out protocol claims like amr, nonce etc.. /// /// The claims. /// protected virtual IEnumerable FilterProtocolClaims(IEnumerable claims) { var claimsToFilter = claims.Where(x => Constants.Filters.ClaimsServiceFilterClaimTypes.Contains(x.Type)); if (claimsToFilter.Any()) { var types = claimsToFilter.Select(x => x.Type); Logger.LogDebug("Claim types from profile service that were filtered: {claimTypes}", types); } return claims.Except(claimsToFilter); } /// /// Filters out protocol claims like amr, nonce etc.. /// /// The claim types. protected virtual IEnumerable FilterRequestedClaimTypes(IEnumerable claimTypes) { var claimTypesToFilter = claimTypes.Where(x => Constants.Filters.ClaimsServiceFilterClaimTypes.Contains(x)); return claimTypes.Except(claimTypesToFilter); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultConsentService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default consent service /// public class DefaultConsentService : IConsentService { /// /// The user consent store /// protected readonly IUserConsentStore UserConsentStore; /// /// The clock /// protected readonly ISystemClock Clock; /// /// The logger /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The clock. /// The user consent store. /// The logger. /// store public DefaultConsentService(ISystemClock clock, IUserConsentStore userConsentStore, ILogger logger) { Clock = clock; UserConsentStore = userConsentStore; Logger = logger; } /// /// Checks if consent is required. /// /// The user. /// The client. /// The parsed scopes. /// /// Boolean if consent is required. /// /// /// client /// or /// subject /// public virtual async Task RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable parsedScopes) { if (client == null) throw new ArgumentNullException(nameof(client)); if (subject == null) throw new ArgumentNullException(nameof(subject)); if (!client.RequireConsent) { Logger.LogDebug("Client is configured to not require consent, no consent is required"); return false; } if (parsedScopes == null || !parsedScopes.Any()) { Logger.LogDebug("No scopes being requested, no consent is required"); return false; } if (!client.AllowRememberConsent) { Logger.LogDebug("Client is configured to not allow remembering consent, consent is required"); return true; } if (parsedScopes.Any(x => x.ParsedName != x.RawValue)) { Logger.LogDebug("Scopes contains parameterized values, consent is required"); return true; } var scopes = parsedScopes.Select(x => x.RawValue).ToArray(); // we always require consent for offline access if // the client has not disabled RequireConsent if (scopes.Contains(IdentityServerConstants.StandardScopes.OfflineAccess)) { Logger.LogDebug("Scopes contains offline_access, consent is required"); return true; } var consent = await UserConsentStore.GetUserConsentAsync(subject.GetSubjectId(), client.ClientId); if (consent == null) { Logger.LogDebug("Found no prior consent from consent store, consent is required"); return true; } if (consent.Expiration.HasExpired(Clock.UtcNow.UtcDateTime)) { Logger.LogDebug("Consent found in consent store is expired, consent is required"); await UserConsentStore.RemoveUserConsentAsync(consent.SubjectId, consent.ClientId); return true; } if (consent.Scopes != null) { var intersect = scopes.Intersect(consent.Scopes); var different = scopes.Count() != intersect.Count(); if (different) { Logger.LogDebug("Consent found in consent store is different than current request, consent is required"); } else { Logger.LogDebug("Consent found in consent store is same as current request, consent is not required"); } return different; } Logger.LogDebug("Consent found in consent store has no scopes, consent is required"); return true; } /// /// Updates the consent asynchronous. /// /// The client. /// The subject. /// The parsed scopes. /// /// /// client /// or /// subject /// public virtual async Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable parsedScopes) { if (client == null) throw new ArgumentNullException(nameof(client)); if (subject == null) throw new ArgumentNullException(nameof(subject)); if (client.AllowRememberConsent) { var subjectId = subject.GetSubjectId(); var clientId = client.ClientId; var scopes = parsedScopes?.Select(x => x.RawValue).ToArray(); if (scopes != null && scopes.Any()) { Logger.LogDebug("Client allows remembering consent, and consent given. Updating consent store for subject: {subject}", subject.GetSubjectId()); var consent = new Consent { CreationTime = Clock.UtcNow.UtcDateTime, SubjectId = subjectId, ClientId = clientId, Scopes = scopes }; if (client.ConsentLifetime.HasValue) { consent.Expiration = consent.CreationTime.AddSeconds(client.ConsentLifetime.Value); } await UserConsentStore.StoreUserConsentAsync(consent); } else { Logger.LogDebug("Client allows remembering consent, and no scopes provided. Removing consent from consent store for subject: {subject}", subject.GetSubjectId()); await UserConsentStore.RemoveUserConsentAsync(subjectId, clientId); } } } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultCorsPolicyService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default CORS policy service. /// public class DefaultCorsPolicyService : ICorsPolicyService { /// /// Logger /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// public DefaultCorsPolicyService(ILogger logger) { Logger = logger; AllowedOrigins = new HashSet(); } /// /// The list allowed origins that are allowed. /// /// /// The allowed origins. /// public ICollection AllowedOrigins { get; set; } /// /// Gets or sets a value indicating whether all origins are allowed. /// /// /// true if allow all; otherwise, false. /// public bool AllowAll { get; set; } /// /// Determines whether the origin allowed. /// /// The origin. /// public virtual Task IsOriginAllowedAsync(string origin) { if (!String.IsNullOrWhiteSpace(origin)) { if (AllowAll) { Logger.LogDebug("AllowAll true, so origin: {0} is allowed", Ioc.Sanitizer.Log.Sanitize(origin)); return Task.FromResult(true); } if (AllowedOrigins != null) { if (AllowedOrigins.Contains(origin, StringComparer.OrdinalIgnoreCase)) { Logger.LogDebug("AllowedOrigins configured and origin {0} is allowed", Ioc.Sanitizer.Log.Sanitize(origin)); return Task.FromResult(true); } else { Logger.LogDebug("AllowedOrigins configured and origin {0} is not allowed", Ioc.Sanitizer.Log.Sanitize(origin)); } } Logger.LogDebug("Exiting; origin {0} is not allowed", Ioc.Sanitizer.Log.Sanitize(origin)); } return Task.FromResult(false); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultDeviceFlowCodeService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services.Default; /// /// Default wrapper service for IDeviceFlowStore, handling key hashing /// /// public class DefaultDeviceFlowCodeService : IDeviceFlowCodeService { private readonly IDeviceFlowStore _store; private readonly IHandleGenerationService _handleGenerationService; /// /// Initializes a new instance of the class. /// /// The store. /// The handle generation service. public DefaultDeviceFlowCodeService(IDeviceFlowStore store, IHandleGenerationService handleGenerationService) { _store = store; _handleGenerationService = handleGenerationService; } /// /// Stores the device authorization request. /// /// The user code. /// The data. /// public async Task StoreDeviceAuthorizationAsync(string userCode, DeviceCode data) { var deviceCode = await _handleGenerationService.GenerateAsync(); await _store.StoreDeviceAuthorizationAsync(deviceCode.Sha256(), userCode.Sha256(), data); return deviceCode; } /// /// Finds device authorization by user code. /// /// The user code. /// public Task FindByUserCodeAsync(string userCode) { return _store.FindByUserCodeAsync(userCode.Sha256()); } /// /// Finds device authorization by device code. /// /// The device code. /// public Task FindByDeviceCodeAsync(string deviceCode) { return _store.FindByDeviceCodeAsync(deviceCode.Sha256()); } /// /// Updates device authorization, searching by user code. /// /// The user code. /// The data. /// public Task UpdateByUserCodeAsync(string userCode, DeviceCode data) { return _store.UpdateByUserCodeAsync(userCode.Sha256(), data); } /// /// Removes the device authorization, searching by device code. /// /// The device code. /// public Task RemoveByDeviceCodeAsync(string deviceCode) { return _store.RemoveByDeviceCodeAsync(deviceCode.Sha256()); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultDeviceFlowInteractionService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; internal class DefaultDeviceFlowInteractionService : IDeviceFlowInteractionService { private readonly IClientStore _clients; private readonly IUserSession _session; private readonly IDeviceFlowCodeService _devices; private readonly IResourceStore _resourceStore; private readonly IScopeParser _scopeParser; private readonly ILogger _logger; public DefaultDeviceFlowInteractionService( IClientStore clients, IUserSession session, IDeviceFlowCodeService devices, IResourceStore resourceStore, IScopeParser scopeParser, ILogger logger) { _clients = clients; _session = session; _devices = devices; _resourceStore = resourceStore; _scopeParser = scopeParser; _logger = logger; } public async Task GetAuthorizationContextAsync(string userCode) { var deviceAuth = await _devices.FindByUserCodeAsync(userCode); if (deviceAuth == null) return null; var client = await _clients.FindClientByIdAsync(deviceAuth.ClientId); if (client == null) return null; var parsedScopesResult = _scopeParser.ParseScopeValues(deviceAuth.RequestedScopes); var validatedResources = await _resourceStore.CreateResourceValidationResult(parsedScopesResult); return new DeviceFlowAuthorizationRequest { Client = client, ValidatedResources = validatedResources }; } public async Task HandleRequestAsync(string userCode, ConsentResponse consent) { if (userCode == null) throw new ArgumentNullException(nameof(userCode)); if (consent == null) throw new ArgumentNullException(nameof(consent)); var deviceAuth = await _devices.FindByUserCodeAsync(userCode); if (deviceAuth == null) return LogAndReturnError("Invalid user code", "Device authorization failure - user code is invalid"); var client = await _clients.FindClientByIdAsync(deviceAuth.ClientId); if (client == null) return LogAndReturnError("Invalid client", "Device authorization failure - requesting client is invalid"); var subject = await _session.GetUserAsync(); if (subject == null) return LogAndReturnError("No user present in device flow request", "Device authorization failure - no user found"); var sid = await _session.GetSessionIdAsync(); deviceAuth.IsAuthorized = true; deviceAuth.Subject = subject; deviceAuth.SessionId = sid; deviceAuth.Description = consent.Description; deviceAuth.AuthorizedScopes = consent.ScopesValuesConsented; // TODO: Device Flow - Record consent template if (consent.RememberConsent) { //var consentRequest = new ConsentRequest(request, subject); //await _consentMessageStore.WriteAsync(consentRequest.Id, new Message(consent, _clock.UtcNow.UtcDateTime)); } await _devices.UpdateByUserCodeAsync(userCode, deviceAuth); return new DeviceFlowInteractionResult(); } private DeviceFlowInteractionResult LogAndReturnError(string error, string errorDescription = null) { _logger.LogError(errorDescription); return DeviceFlowInteractionResult.Failure(error); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultEventService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Events; /// /// The default event service /// /// public class DefaultEventService : IEventService { /// /// The options /// protected readonly IdentityServerOptions Options; /// /// The context /// protected readonly IHttpContextAccessor Context; /// /// The sink /// protected readonly IEventSink Sink; /// /// The clock /// protected readonly ISystemClock Clock; /// /// Initializes a new instance of the class. /// /// The options. /// The context. /// The sink. /// The clock. public DefaultEventService(IdentityServerOptions options, IHttpContextAccessor context, IEventSink sink, ISystemClock clock) { Options = options; Context = context; Sink = sink; Clock = clock; } /// /// Raises the specified event. /// /// The event. /// /// evt public async Task RaiseAsync(Event evt) { if (evt == null) throw new ArgumentNullException(nameof(evt)); if (CanRaiseEvent(evt)) { await PrepareEventAsync(evt); await Sink.PersistAsync(evt); } } /// /// Indicates if the type of event will be persisted. /// /// /// /// public bool CanRaiseEventType(EventTypes evtType) { switch (evtType) { case EventTypes.Failure: return Options.Events.RaiseFailureEvents; case EventTypes.Information: return Options.Events.RaiseInformationEvents; case EventTypes.Success: return Options.Events.RaiseSuccessEvents; case EventTypes.Error: return Options.Events.RaiseErrorEvents; default: throw new ArgumentOutOfRangeException(nameof(evtType)); } } /// /// Determines whether this event would be persisted. /// /// The evt. /// /// true if this event would be persisted; otherwise, false. /// protected virtual bool CanRaiseEvent(Event evt) { return CanRaiseEventType(evt.EventType); } /// /// Prepares the event. /// /// The evt. /// protected virtual async Task PrepareEventAsync(Event evt) { evt.ActivityId = Context.HttpContext.TraceIdentifier; evt.TimeStamp = Clock.UtcNow.UtcDateTime; evt.ProcessId = Process.GetCurrentProcess().Id; if (Context.HttpContext.Connection.LocalIpAddress != null) { evt.LocalIpAddress = Context.HttpContext.Connection.LocalIpAddress.ToString() + ":" + Context.HttpContext.Connection.LocalPort; } else { evt.LocalIpAddress = "unknown"; } if (Context.HttpContext.Connection.RemoteIpAddress != null) { evt.RemoteIpAddress = Context.HttpContext.Connection.RemoteIpAddress.ToString(); } else { evt.RemoteIpAddress = "unknown"; } await evt.PrepareAsync(); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultEventSink.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default implementation of the event service. Write events raised to the log. /// public class DefaultEventSink : IEventSink { /// /// The logger /// private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The logger. public DefaultEventSink(ILogger logger) { _logger = logger; } /// /// Raises the specified event. /// /// The event. /// evt public virtual Task PersistAsync(Event evt) { if (evt == null) throw new ArgumentNullException(nameof(evt)); _logger.LogInformation("{@event}", evt); return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultHandleGenerationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default handle generation service /// /// public class DefaultHandleGenerationService : IHandleGenerationService { /// /// Generates a handle. /// /// The length. /// public Task GenerateAsync(int length) { return Task.FromResult(CryptoRandom.CreateUniqueId(length, CryptoRandom.OutputFormat.Hex)); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultIdentityServerInteractionService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; internal class DefaultIdentityServerInteractionService : IIdentityServerInteractionService { private readonly ISystemClock _clock; private readonly IHttpContextAccessor _context; private readonly IMessageStore _logoutMessageStore; private readonly IMessageStore _errorMessageStore; private readonly IConsentMessageStore _consentMessageStore; private readonly IPersistedGrantService _grants; private readonly IUserSession _userSession; private readonly ILogger _logger; private readonly ReturnUrlParser _returnUrlParser; public DefaultIdentityServerInteractionService( ISystemClock clock, IHttpContextAccessor context, IMessageStore logoutMessageStore, IMessageStore errorMessageStore, IConsentMessageStore consentMessageStore, IPersistedGrantService grants, IUserSession userSession, ReturnUrlParser returnUrlParser, ILogger logger) { _clock = clock; _context = context; _logoutMessageStore = logoutMessageStore; _errorMessageStore = errorMessageStore; _consentMessageStore = consentMessageStore; _grants = grants; _userSession = userSession; _returnUrlParser = returnUrlParser; _logger = logger; } public async Task GetAuthorizationContextAsync(string returnUrl) { var result = await _returnUrlParser.ParseAsync(returnUrl); if (result != null) { _logger.LogTrace("AuthorizationRequest being returned"); } else { _logger.LogTrace("No AuthorizationRequest being returned"); } return result; } public async Task GetLogoutContextAsync(string logoutId) { var msg = await _logoutMessageStore.ReadAsync(logoutId); var iframeUrl = await _context.HttpContext.GetIdentityServerSignoutFrameCallbackUrlAsync(msg?.Data); return new LogoutRequest(iframeUrl, msg?.Data); } public async Task CreateLogoutContextAsync() { var user = await _userSession.GetUserAsync(); if (user != null) { var clientIds = await _userSession.GetClientListAsync(); if (clientIds.Any()) { var sid = await _userSession.GetSessionIdAsync(); var msg = new Message(new LogoutMessage { SubjectId = user?.GetSubjectId(), SessionId = sid, ClientIds = clientIds }, _clock.UtcNow.UtcDateTime); var id = await _logoutMessageStore.WriteAsync(msg); return id; } } return null; } public async Task GetErrorContextAsync(string errorId) { if (errorId != null) { var result = await _errorMessageStore.ReadAsync(errorId); var data = result?.Data; if (data != null) { _logger.LogTrace("Error context loaded"); } else { _logger.LogTrace("No error context found"); } return data; } _logger.LogTrace("No error context found"); return null; } public async Task GrantConsentAsync(AuthorizationRequest request, ConsentResponse consent, string subject = null) { if (subject == null) { var user = await _userSession.GetUserAsync(); subject = user?.GetSubjectId(); } if (subject == null && consent.Granted) { throw new ArgumentNullException(nameof(subject), "User is not currently authenticated, and no subject id passed"); } var consentRequest = new ConsentRequest(request, subject); await _consentMessageStore.WriteAsync(consentRequest.Id, new Message(consent, _clock.UtcNow.UtcDateTime)); } public Task DenyAuthorizationAsync(AuthorizationRequest request, AuthorizationError error, string errorDescription = null) { var response = new ConsentResponse { Error = error, ErrorDescription = errorDescription }; return GrantConsentAsync(request, response); } public bool IsValidReturnUrl(string returnUrl) { var result = _returnUrlParser.IsValidReturnUrl(returnUrl); if (result) { _logger.LogTrace("IsValidReturnUrl true"); } else { _logger.LogTrace("IsValidReturnUrl false"); } return result; } public async Task> GetAllUserGrantsAsync() { var user = await _userSession.GetUserAsync(); if (user != null) { var subject = user.GetSubjectId(); return await _grants.GetAllGrantsAsync(subject); } return Enumerable.Empty(); } public async Task RevokeUserConsentAsync(string clientId) { var user = await _userSession.GetUserAsync(); if (user != null) { var subject = user.GetSubjectId(); await _grants.RemoveAllGrantsAsync(subject, clientId); } } public async Task RevokeTokensForCurrentSessionAsync() { var user = await _userSession.GetUserAsync(); if (user != null) { var subject = user.GetSubjectId(); var sessionId = await _userSession.GetSessionIdAsync(); await _grants.RemoveAllGrantsAsync(subject, sessionId: sessionId); } } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultJwtRequestUriHttpClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default JwtRequest client /// public class DefaultJwtRequestUriHttpClient : IJwtRequestUriHttpClient { private readonly HttpClient _client; private readonly IdentityServerOptions _options; private readonly ILogger _logger; /// /// ctor /// /// An HTTP client /// The options. /// The logger factory public DefaultJwtRequestUriHttpClient(HttpClient client, IdentityServerOptions options, ILoggerFactory loggerFactory) { _client = client; _options = options; _logger = loggerFactory.CreateLogger(); } /// public async Task GetJwtAsync(string url, Client client) { var req = new HttpRequestMessage(HttpMethod.Get, url); req.Properties.Add(IdentityServerConstants.JwtRequestClientKey, client); var response = await _client.SendAsync(req); if (response.StatusCode == System.Net.HttpStatusCode.OK) { if (_options.StrictJarValidation) { if (!string.Equals(response.Content.Headers.ContentType.MediaType, $"application/{JwtClaimTypes.JwtTypes.AuthorizationRequest}", StringComparison.Ordinal)) { _logger.LogError("Invalid content type {type} from jwt url {url}", response.Content.Headers.ContentType.MediaType, url); return null; } } _logger.LogDebug("Success http response from jwt url {url}", url); var json = await response.Content.ReadAsStringAsync(); return json; } _logger.LogError("Invalid http status code {status} from jwt url {url}", response.StatusCode, url); return null; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultKeyMaterialService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Services; /// /// The default key material service /// /// public class DefaultKeyMaterialService : IKeyMaterialService { private readonly IEnumerable _signingCredentialStores; private readonly IEnumerable _validationKeysStores; /// /// Initializes a new instance of the class. /// /// The validation keys stores. /// The signing credential store. public DefaultKeyMaterialService(IEnumerable validationKeysStores, IEnumerable signingCredentialStores) { _signingCredentialStores = signingCredentialStores; _validationKeysStores = validationKeysStores; } /// public async Task GetSigningCredentialsAsync(IEnumerable allowedAlgorithms = null) { if (_signingCredentialStores.Any()) { if (allowedAlgorithms.EnumerableIsNullOrEmpty()) { return await _signingCredentialStores.First().GetSigningCredentialsAsync(); } var credential = (await GetAllSigningCredentialsAsync()).FirstOrDefault(c => allowedAlgorithms.Contains(c.Algorithm)); if (credential is null) { throw new InvalidOperationException($"No signing credential for algorithms ({allowedAlgorithms.ToSpaceSeparatedString()}) registered."); } return credential; } return null; } /// public async Task> GetAllSigningCredentialsAsync() { var credentials = new List(); foreach (var store in _signingCredentialStores) { credentials.Add(await store.GetSigningCredentialsAsync()); } return credentials; } /// public async Task> GetValidationKeysAsync() { var keys = new List(); foreach (var store in _validationKeysStores) { keys.AddRange(await store.GetValidationKeysAsync()); } return keys; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultPersistedGrantService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default persisted grant service /// public class DefaultPersistedGrantService : IPersistedGrantService { private readonly ILogger _logger; private readonly IPersistedGrantStore _store; private readonly IPersistentGrantSerializer _serializer; /// /// Initializes a new instance of the class. /// /// The store. /// The serializer. /// The logger. public DefaultPersistedGrantService(IPersistedGrantStore store, IPersistentGrantSerializer serializer, ILogger logger) { _store = store; _serializer = serializer; _logger = logger; } /// public async Task> GetAllGrantsAsync(string subjectId) { if (String.IsNullOrWhiteSpace(subjectId)) throw new ArgumentNullException(nameof(subjectId)); var grants = (await _store.GetAllAsync(new PersistedGrantFilter { SubjectId = subjectId })).ToArray(); try { var consents = grants.Where(x => x.Type == IdentityServerConstants.PersistedGrantTypes.UserConsent) .Select(x => _serializer.Deserialize(x.Data)) .Select(x => new Grant { ClientId = x.ClientId, SubjectId = subjectId, Scopes = x.Scopes, CreationTime = x.CreationTime, Expiration = x.Expiration }); var codes = grants.Where(x => x.Type == IdentityServerConstants.PersistedGrantTypes.AuthorizationCode) .Select(x => _serializer.Deserialize(x.Data)) .Select(x => new Grant { ClientId = x.ClientId, SubjectId = subjectId, Description = x.Description, Scopes = x.RequestedScopes, CreationTime = x.CreationTime, Expiration = x.CreationTime.AddSeconds(x.Lifetime) }); var refresh = grants.Where(x => x.Type == IdentityServerConstants.PersistedGrantTypes.RefreshToken) .Select(x => _serializer.Deserialize(x.Data)) .Select(x => new Grant { ClientId = x.ClientId, SubjectId = subjectId, Description = x.Description, Scopes = x.Scopes, CreationTime = x.CreationTime, Expiration = x.CreationTime.AddSeconds(x.Lifetime) }); var access = grants.Where(x => x.Type == IdentityServerConstants.PersistedGrantTypes.ReferenceToken) .Select(x => _serializer.Deserialize(x.Data)) .Select(x => new Grant { ClientId = x.ClientId, SubjectId = subjectId, Description = x.Description, Scopes = x.Scopes, CreationTime = x.CreationTime, Expiration = x.CreationTime.AddSeconds(x.Lifetime) }); consents = Join(consents, codes); consents = Join(consents, refresh); consents = Join(consents, access); return consents.ToArray(); } catch (Exception ex) { _logger.LogError(ex, "Failed processing results from grant store."); } return Enumerable.Empty(); } private IEnumerable Join(IEnumerable first, IEnumerable second) { var list = first.ToList(); foreach(var other in second) { var match = list.FirstOrDefault(x => x.ClientId == other.ClientId); if (match != null) { match.Scopes = match.Scopes.Union(other.Scopes).Distinct(); if (match.CreationTime > other.CreationTime) { // show the earlier creation time match.CreationTime = other.CreationTime; } if (match.Expiration == null || other.Expiration == null) { // show that there is no expiration to one of the grants match.Expiration = null; } else if (match.Expiration < other.Expiration) { // show the latest expiration match.Expiration = other.Expiration; } match.Description = match.Description ?? other.Description; } else { list.Add(other); } } return list; } /// public Task RemoveAllGrantsAsync(string subjectId, string clientId = null, string sessionId = null) { if (String.IsNullOrWhiteSpace(subjectId)) throw new ArgumentNullException(nameof(subjectId)); return _store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = subjectId, ClientId = clientId, SessionId = sessionId }); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultProfileService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default profile service implementation. /// This implementation sources all claims from the current subject (e.g. the cookie). /// /// public class DefaultProfileService : IProfileService { /// /// The logger /// protected readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The logger. public DefaultProfileService(ILogger logger) { Logger = logger; } /// /// This method is called whenever claims about the user are requested (e.g. during token creation or via the userinfo endpoint) /// /// The context. /// public virtual Task GetProfileDataAsync(ProfileDataRequestContext context) { context.LogProfileRequest(Logger); context.AddRequestedClaims(context.Subject.Claims); context.LogIssuedClaims(Logger); return Task.CompletedTask; } /// /// This method gets called whenever identity server needs to determine if the user is valid or active (e.g. if the user's account has been deactivated since they logged in). /// (e.g. during token issuance or validation). /// /// The context. /// public virtual Task IsActiveAsync(IsActiveContext context) { Logger.LogDebug("IsActive called from: {caller}", context.Caller); context.IsActive = true; return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultRefreshTokenService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default refresh token service /// public class DefaultRefreshTokenService : IRefreshTokenService { /// /// The logger /// protected readonly ILogger Logger; /// /// The refresh token store /// protected IRefreshTokenStore RefreshTokenStore { get; } /// /// The profile service /// protected IProfileService Profile { get; } /// /// The clock /// protected ISystemClock Clock { get; } /// /// Initializes a new instance of the class. /// /// The refresh token store /// /// The clock /// The logger public DefaultRefreshTokenService(IRefreshTokenStore refreshTokenStore, IProfileService profile, ISystemClock clock, ILogger logger) { RefreshTokenStore = refreshTokenStore; Profile = profile; Clock = clock; Logger = logger; } /// /// Validates a refresh token /// /// The token handle. /// The client. /// public virtual async Task ValidateRefreshTokenAsync(string tokenHandle, Client client) { var invalidGrant = new TokenValidationResult { IsError = true, Error = OidcConstants.TokenErrors.InvalidGrant }; Logger.LogTrace("Start refresh token validation"); ///////////////////////////////////////////// // check if refresh token is valid ///////////////////////////////////////////// var refreshToken = await RefreshTokenStore.GetRefreshTokenAsync(tokenHandle); if (refreshToken == null) { Logger.LogWarning("Invalid refresh token"); return invalidGrant; } ///////////////////////////////////////////// // check if refresh token has expired ///////////////////////////////////////////// if (refreshToken.CreationTime.HasExceeded(refreshToken.Lifetime, Clock.UtcNow.DateTime)) { Logger.LogWarning("Refresh token has expired."); return invalidGrant; } ///////////////////////////////////////////// // check if client belongs to requested refresh token ///////////////////////////////////////////// if (client.ClientId != refreshToken.ClientId) { Logger.LogError("{0} tries to refresh token belonging to {1}", client.ClientId, refreshToken.ClientId); return invalidGrant; } ///////////////////////////////////////////// // check if client still has offline_access scope ///////////////////////////////////////////// if (!client.AllowOfflineAccess) { Logger.LogError("{clientId} does not have access to offline_access scope anymore", client.ClientId); return invalidGrant; } ///////////////////////////////////////////// // check if refresh token has been consumed ///////////////////////////////////////////// if (refreshToken.ConsumedTime.HasValue) { if ((await AcceptConsumedTokenAsync(refreshToken)) == false) { Logger.LogWarning("Rejecting refresh token because it has been consumed already."); return invalidGrant; } } ///////////////////////////////////////////// // make sure user is enabled ///////////////////////////////////////////// var isActiveCtx = new IsActiveContext( refreshToken.Subject, client, IdentityServerConstants.ProfileIsActiveCallers.RefreshTokenValidation); await Profile.IsActiveAsync(isActiveCtx); if (isActiveCtx.IsActive == false) { Logger.LogError("{subjectId} has been disabled", refreshToken.Subject.GetSubjectId()); return invalidGrant; } return new TokenValidationResult { IsError = false, RefreshToken = refreshToken, Client = client }; } /// /// Callback to decide if an already consumed token should be accepted. /// /// /// protected virtual Task AcceptConsumedTokenAsync(RefreshToken refreshToken) { // by default we will not accept consumed tokens // change the behavior here to implement a time window // you can also implement additional revocation logic here return Task.FromResult(false); } /// /// Creates the refresh token. /// /// The subject. /// The access token. /// The client. /// /// The refresh token handle /// public virtual async Task CreateRefreshTokenAsync(ClaimsPrincipal subject, Token accessToken, Client client) { Logger.LogDebug("Creating refresh token"); int lifetime; if (client.RefreshTokenExpiration == TokenExpiration.Absolute) { Logger.LogDebug("Setting an absolute lifetime: {absoluteLifetime}", client.AbsoluteRefreshTokenLifetime); lifetime = client.AbsoluteRefreshTokenLifetime; } else { lifetime = client.SlidingRefreshTokenLifetime; if (client.AbsoluteRefreshTokenLifetime > 0 && lifetime > client.AbsoluteRefreshTokenLifetime) { Logger.LogWarning( "Client {clientId}'s configured " + nameof(client.SlidingRefreshTokenLifetime) + " of {slidingLifetime} exceeds its " + nameof(client.AbsoluteRefreshTokenLifetime) + " of {absoluteLifetime}. The refresh_token's sliding lifetime will be capped to the absolute lifetime", client.ClientId, lifetime, client.AbsoluteRefreshTokenLifetime); lifetime = client.AbsoluteRefreshTokenLifetime; } Logger.LogDebug("Setting a sliding lifetime: {slidingLifetime}", lifetime); } var refreshToken = new RefreshToken { CreationTime = Clock.UtcNow.UtcDateTime, Lifetime = lifetime, AccessToken = accessToken }; var handle = await RefreshTokenStore.StoreRefreshTokenAsync(refreshToken); return handle; } /// /// Updates the refresh token. /// /// The handle. /// The refresh token. /// The client. /// /// The refresh token handle /// public virtual async Task UpdateRefreshTokenAsync(string handle, RefreshToken refreshToken, Client client) { Logger.LogDebug("Updating refresh token"); bool needsCreate = false; bool needsUpdate = false; if (client.RefreshTokenUsage == TokenUsage.OneTimeOnly) { Logger.LogDebug("Token usage is one-time only. Setting current handle as consumed, and generating new handle"); // flag as consumed if (refreshToken.ConsumedTime == null) { refreshToken.ConsumedTime = Clock.UtcNow.UtcDateTime; await RefreshTokenStore.UpdateRefreshTokenAsync(handle, refreshToken); } // create new one needsCreate = true; } if (client.RefreshTokenExpiration == TokenExpiration.Sliding) { Logger.LogDebug("Refresh token expiration is sliding - extending lifetime"); // if absolute exp > 0, make sure we don't exceed absolute exp // if absolute exp = 0, allow indefinite slide var currentLifetime = refreshToken.CreationTime.GetLifetimeInSeconds(Clock.UtcNow.UtcDateTime); Logger.LogDebug("Current lifetime: {currentLifetime}", currentLifetime.ToString()); var newLifetime = currentLifetime + client.SlidingRefreshTokenLifetime; Logger.LogDebug("New lifetime: {slidingLifetime}", newLifetime.ToString()); // zero absolute refresh token lifetime represents unbounded absolute lifetime // if absolute lifetime > 0, cap at absolute lifetime if (client.AbsoluteRefreshTokenLifetime > 0 && newLifetime > client.AbsoluteRefreshTokenLifetime) { newLifetime = client.AbsoluteRefreshTokenLifetime; Logger.LogDebug("New lifetime exceeds absolute lifetime, capping it to {newLifetime}", newLifetime.ToString()); } refreshToken.Lifetime = newLifetime; needsUpdate = true; } if (needsCreate) { // set it to null so that we save non-consumed token refreshToken.ConsumedTime = null; handle = await RefreshTokenStore.StoreRefreshTokenAsync(refreshToken); Logger.LogDebug("Created refresh token in store"); } else if (needsUpdate) { await RefreshTokenStore.UpdateRefreshTokenAsync(handle, refreshToken); Logger.LogDebug("Updated refresh token in store"); } else { Logger.LogDebug("No updates to refresh token done"); } return handle; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultReplayCache.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default implementation of the replay cache using IDistributedCache /// public class DefaultReplayCache : IReplayCache { private const string Prefix = nameof(DefaultReplayCache) + ":"; private readonly IDistributedCache _cache; /// /// ctor /// /// public DefaultReplayCache(IDistributedCache cache) { _cache = cache; } /// public async Task AddAsync(string purpose, string handle, DateTimeOffset expiration) { var options = new DistributedCacheEntryOptions { AbsoluteExpiration = expiration }; await _cache.SetAsync(Prefix + purpose + handle, new byte[] { }, options); } /// public async Task ExistsAsync(string purpose, string handle) { return (await _cache.GetAsync(Prefix + purpose + handle, default)) != null; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultTokenCreationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Services; /// /// Default token creation service /// public class DefaultTokenCreationService : ITokenCreationService { /// /// The key service /// protected readonly IKeyMaterialService Keys; /// /// The logger /// protected readonly ILogger Logger; /// /// The clock /// protected readonly ISystemClock Clock; /// /// The options /// protected readonly IdentityServerOptions Options; /// /// Initializes a new instance of the class. /// /// The options. /// The keys. /// The options. /// The logger. public DefaultTokenCreationService( ISystemClock clock, IKeyMaterialService keys, IdentityServerOptions options, ILogger logger) { Clock = clock; Keys = keys; Options = options; Logger = logger; } /// /// Creates the token. /// /// The token. /// /// A protected and serialized security token /// public virtual async Task CreateTokenAsync(Token token) { var header = await CreateHeaderAsync(token); var payload = await CreatePayloadAsync(token); return await CreateJwtAsync(new JwtSecurityToken(header, payload)); } /// /// Creates the JWT header /// /// The token. /// The JWT header protected virtual async Task CreateHeaderAsync(Token token) { var credential = await Keys.GetSigningCredentialsAsync(token.AllowedSigningAlgorithms); if (credential == null) { throw new InvalidOperationException("No signing credential is configured. Can't create JWT token"); } var header = new JwtHeader(credential); // emit x5t claim for backwards compatibility with v4 of MS JWT library if (credential.Key is X509SecurityKey x509Key) { var cert = x509Key.Certificate; if (Clock.UtcNow.UtcDateTime > cert.NotAfter) { Logger.LogWarning("Certificate {subjectName} has expired on {expiration}", cert.Subject, cert.NotAfter.ToString(CultureInfo.InvariantCulture)); } header["x5t"] = Base64Url.Encode(cert.GetCertHash()); } if (token.Type == TokenTypes.AccessToken) { if (Options.AccessTokenJwtType.IsPresent()) { header["typ"] = Options.AccessTokenJwtType; } } return header; } /// /// Creates the JWT payload /// /// The token. /// The JWT payload protected virtual Task CreatePayloadAsync(Token token) { var payload = token.CreateJwtPayload(Clock, Options, Logger); return Task.FromResult(payload); } /// /// Applies the signature to the JWT /// /// The JWT object. /// The signed JWT protected virtual Task CreateJwtAsync(JwtSecurityToken jwt) { var handler = new JwtSecurityTokenHandler(); return Task.FromResult(handler.WriteToken(jwt)); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultTokenService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default token service /// public class DefaultTokenService : ITokenService { /// /// The logger /// protected readonly ILogger Logger; /// /// The HTTP context accessor /// protected readonly IHttpContextAccessor ContextAccessor; /// /// The claims provider /// protected readonly IClaimsService ClaimsProvider; /// /// The reference token store /// protected readonly IReferenceTokenStore ReferenceTokenStore; /// /// The signing service /// protected readonly ITokenCreationService CreationService; /// /// The clock /// protected readonly ISystemClock Clock; /// /// The key material service /// protected readonly IKeyMaterialService KeyMaterialService; /// /// The IdentityServer options /// protected readonly IdentityServerOptions Options; /// /// Initializes a new instance of the class. /// /// The claims provider. /// The reference token store. /// The signing service. /// The HTTP context accessor. /// The clock. /// /// The IdentityServer options /// The logger. public DefaultTokenService( IClaimsService claimsProvider, IReferenceTokenStore referenceTokenStore, ITokenCreationService creationService, IHttpContextAccessor contextAccessor, ISystemClock clock, IKeyMaterialService keyMaterialService, IdentityServerOptions options, ILogger logger) { ContextAccessor = contextAccessor; ClaimsProvider = claimsProvider; ReferenceTokenStore = referenceTokenStore; CreationService = creationService; Clock = clock; KeyMaterialService = keyMaterialService; Options = options; Logger = logger; } /// /// Creates an identity token. /// /// The token creation request. /// /// An identity token /// public virtual async Task CreateIdentityTokenAsync(TokenCreationRequest request) { Logger.LogTrace("Creating identity token"); request.Validate(); // todo: Dom, add a test for this. validate the at and c hashes are correct for the id_token when the client's alg doesn't match the server default. var credential = await KeyMaterialService.GetSigningCredentialsAsync(request.ValidatedRequest.Client.AllowedIdentityTokenSigningAlgorithms); if (credential == null) { throw new InvalidOperationException("No signing credential is configured."); } var signingAlgorithm = credential.Algorithm; // host provided claims var claims = new List(); // if nonce was sent, must be mirrored in id token if (request.Nonce.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.Nonce, request.Nonce)); } // add iat claim claims.Add(new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)); // add at_hash claim if (request.AccessTokenToHash.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.AccessTokenHash, CryptoHelper.CreateHashClaimValue(request.AccessTokenToHash, signingAlgorithm))); } // add c_hash claim if (request.AuthorizationCodeToHash.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.AuthorizationCodeHash, CryptoHelper.CreateHashClaimValue(request.AuthorizationCodeToHash, signingAlgorithm))); } // add s_hash claim if (request.StateHash.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.StateHash, request.StateHash)); } // add sid if present if (request.ValidatedRequest.SessionId.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.SessionId, request.ValidatedRequest.SessionId)); } claims.AddRange(await ClaimsProvider.GetIdentityTokenClaimsAsync( request.Subject, request.ValidatedResources, request.IncludeAllIdentityClaims, request.ValidatedRequest)); var issuer = ContextAccessor.HttpContext.GetIdentityServerIssuerUri(); var token = new Token(OidcConstants.TokenTypes.IdentityToken) { CreationTime = Clock.UtcNow.UtcDateTime, Audiences = { request.ValidatedRequest.Client.ClientId }, Issuer = issuer, Lifetime = request.ValidatedRequest.Client.IdentityTokenLifetime, Claims = claims.Distinct(new ClaimComparer()).ToList(), ClientId = request.ValidatedRequest.Client.ClientId, AccessTokenType = request.ValidatedRequest.AccessTokenType, AllowedSigningAlgorithms = request.ValidatedRequest.Client.AllowedIdentityTokenSigningAlgorithms }; return token; } /// /// Creates an access token. /// /// The token creation request. /// /// An access token /// public virtual async Task CreateAccessTokenAsync(TokenCreationRequest request) { Logger.LogTrace("Creating access token"); request.Validate(); var claims = new List(); claims.AddRange(await ClaimsProvider.GetAccessTokenClaimsAsync( request.Subject, request.ValidatedResources, request.ValidatedRequest)); if (request.ValidatedRequest.Client.IncludeJwtId) { claims.Add(new Claim(JwtClaimTypes.JwtId, CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex))); } if (request.ValidatedRequest.SessionId.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.SessionId, request.ValidatedRequest.SessionId)); } // iat claim as required by JWT profile claims.Add(new Claim(JwtClaimTypes.IssuedAt, Clock.UtcNow.ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64)); var issuer = ContextAccessor.HttpContext.GetIdentityServerIssuerUri(); var token = new Token(OidcConstants.TokenTypes.AccessToken) { CreationTime = Clock.UtcNow.UtcDateTime, Issuer = issuer, Lifetime = request.ValidatedRequest.AccessTokenLifetime, Claims = claims.Distinct(new ClaimComparer()).ToList(), ClientId = request.ValidatedRequest.Client.ClientId, Description = request.Description, AccessTokenType = request.ValidatedRequest.AccessTokenType, AllowedSigningAlgorithms = request.ValidatedResources.Resources.ApiResources.FindMatchingSigningAlgorithms() }; // add aud based on ApiResources in the validated request foreach (var aud in request.ValidatedResources.Resources.ApiResources.Select(x => x.Name).Distinct()) { token.Audiences.Add(aud); } if (Options.EmitStaticAudienceClaim) { token.Audiences.Add(string.Format(IdentityServerConstants.AccessTokenAudience, issuer.EnsureTrailingSlash())); } // add cnf if present if (request.ValidatedRequest.Confirmation.IsPresent()) { token.Confirmation = request.ValidatedRequest.Confirmation; } else { if (Options.MutualTls.AlwaysEmitConfirmationClaim) { var clientCertificate = await ContextAccessor.HttpContext.Connection.GetClientCertificateAsync(); if (clientCertificate != null) { token.Confirmation = clientCertificate.CreateThumbprintCnf(); } } } return token; } /// /// Creates a serialized and protected security token. /// /// The token. /// /// A security token in serialized form /// /// Invalid token type. public virtual async Task CreateSecurityTokenAsync(Token token) { string tokenResult; if (token.Type == OidcConstants.TokenTypes.AccessToken) { if (token.AccessTokenType == AccessTokenType.Jwt) { Logger.LogTrace("Creating JWT access token"); tokenResult = await CreationService.CreateTokenAsync(token); } else { Logger.LogTrace("Creating reference access token"); var handle = await ReferenceTokenStore.StoreReferenceTokenAsync(token); tokenResult = handle; } } else if (token.Type == OidcConstants.TokenTypes.IdentityToken) { Logger.LogTrace("Creating JWT identity token"); tokenResult = await CreationService.CreateTokenAsync(token); } else { throw new InvalidOperationException("Invalid token type."); } return tokenResult; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultUserCodeService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default user code service implementation. /// /// public class DefaultUserCodeService : IUserCodeService { private readonly IEnumerable _generators; /// /// Initializes a new instance of the class. /// /// The generators. /// generators public DefaultUserCodeService(IEnumerable generators) { _generators = generators ?? throw new ArgumentNullException(nameof(generators)); } /// /// Gets the user code generator. /// /// Type of user code. /// public Task GetGenerator(string userCodeType) => Task.FromResult(_generators.FirstOrDefault(x => x.UserCodeType == userCodeType)); } ================================================ FILE: src/IdentityServer8/src/Services/Default/DefaultUserSession.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Cookie-based session implementation /// /// public class DefaultUserSession : IUserSession { /// /// The HTTP context accessor /// protected readonly IHttpContextAccessor HttpContextAccessor; /// /// The handlers /// protected readonly IAuthenticationHandlerProvider Handlers; /// /// The options /// protected readonly IdentityServerOptions Options; /// /// The clock /// protected readonly ISystemClock Clock; /// /// The logger /// protected readonly ILogger Logger; /// /// Gets the HTTP context. /// /// /// The HTTP context. /// protected HttpContext HttpContext => HttpContextAccessor.HttpContext; /// /// Gets the name of the check session cookie. /// /// /// The name of the check session cookie. /// protected string CheckSessionCookieName => Options.Authentication.CheckSessionCookieName; /// /// Gets the domain of the check session cookie. /// /// /// The domain of the check session cookie. /// protected string CheckSessionCookieDomain => Options.Authentication.CheckSessionCookieDomain; /// /// Gets the SameSite mode of the check session cookie. /// /// /// The SameSite mode of the check session cookie. /// protected SameSiteMode CheckSessionCookieSameSiteMode => Options.Authentication.CheckSessionCookieSameSiteMode; /// /// The principal /// protected ClaimsPrincipal Principal; /// /// The properties /// protected AuthenticationProperties Properties; /// /// Initializes a new instance of the class. /// /// The HTTP context accessor. /// The handlers. /// The options. /// The clock. /// The logger. public DefaultUserSession( IHttpContextAccessor httpContextAccessor, IAuthenticationHandlerProvider handlers, IdentityServerOptions options, ISystemClock clock, ILogger logger) { HttpContextAccessor = httpContextAccessor; Handlers = handlers; Options = options; Clock = clock; Logger = logger; } // we need this helper (and can't call HttpContext.AuthenticateAsync) so we don't run // claims transformation when we get the principal. this also ensures that we don't // re-issue a cookie that includes the claims from claims transformation. // // also, by caching the _principal/_properties it allows someone to issue a new // cookie (via HttpContext.SignInAsync) and we'll use those new values, rather than // just reading the incoming cookie // // this design requires this to be in DI as scoped /// /// Authenticates the authentication cookie for the current HTTP request and caches the user and properties results. /// protected virtual async Task AuthenticateAsync() { if (Principal == null || Properties == null) { var scheme = await HttpContext.GetCookieAuthenticationSchemeAsync(); var handler = await Handlers.GetHandlerAsync(HttpContext, scheme); if (handler == null) { throw new InvalidOperationException($"No authentication handler is configured to authenticate for the scheme: {scheme}"); } var result = await handler.AuthenticateAsync(); if (result != null && result.Succeeded) { Principal = result.Principal; Properties = result.Properties; } } } /// /// Creates a session identifier for the signin context and issues the session id cookie. /// /// /// /// /// /// principal /// or /// properties /// public virtual async Task CreateSessionIdAsync(ClaimsPrincipal principal, AuthenticationProperties properties) { if (principal == null) throw new ArgumentNullException(nameof(principal)); if (properties == null) throw new ArgumentNullException(nameof(properties)); var currentSubjectId = (await GetUserAsync())?.GetSubjectId(); var newSubjectId = principal.GetSubjectId(); if (properties.GetSessionId() == null || currentSubjectId != newSubjectId) { properties.SetSessionId(CryptoRandom.CreateUniqueId(16, CryptoRandom.OutputFormat.Hex)); } var sid = properties.GetSessionId(); IssueSessionIdCookie(sid); Principal = principal; Properties = properties; return sid; } /// /// Gets the current authenticated user. /// /// public virtual async Task GetUserAsync() { await AuthenticateAsync(); return Principal; } /// /// Gets the current session identifier. /// /// public virtual async Task GetSessionIdAsync() { await AuthenticateAsync(); return Properties?.GetSessionId(); } /// /// Ensures the session identifier cookie asynchronous. /// /// public virtual async Task EnsureSessionIdCookieAsync() { var sid = await GetSessionIdAsync(); if (sid != null) { IssueSessionIdCookie(sid); } else { await RemoveSessionIdCookieAsync(); } } /// /// Removes the session identifier cookie. /// /// public virtual Task RemoveSessionIdCookieAsync() { if (HttpContext.Request.Cookies.ContainsKey(CheckSessionCookieName)) { // only remove it if we have it in the request var options = CreateSessionIdCookieOptions(); options.Expires = Clock.UtcNow.UtcDateTime.AddYears(-1); HttpContext.Response.Cookies.Append(CheckSessionCookieName, ".", options); } return Task.CompletedTask; } /// /// Creates the options for the session cookie. /// public virtual CookieOptions CreateSessionIdCookieOptions() { var secure = HttpContext.Request.IsHttps; var path = HttpContext.GetIdentityServerBasePath().CleanUrlPath(); var options = new CookieOptions { HttpOnly = false, Secure = secure, Path = path, IsEssential = true, Domain = CheckSessionCookieDomain, SameSite = CheckSessionCookieSameSiteMode }; return options; } /// /// Issues the cookie that contains the session id. /// /// public virtual void IssueSessionIdCookie(string sid) { if (Options.Endpoints.EnableCheckSessionEndpoint) { if (HttpContext.Request.Cookies[CheckSessionCookieName] != sid) { HttpContext.Response.Cookies.Append( Options.Authentication.CheckSessionCookieName, sid, CreateSessionIdCookieOptions()); } } } /// /// Adds a client to the list of clients the user has signed into during their session. /// /// The client identifier. /// /// clientId public virtual async Task AddClientIdAsync(string clientId) { if (clientId == null) throw new ArgumentNullException(nameof(clientId)); await AuthenticateAsync(); if (Properties != null) { var clientIds = Properties.GetClientList(); if (!clientIds.Contains(clientId)) { Properties.AddClientId(clientId); await UpdateSessionCookie(); } } } /// /// Gets the list of clients the user has signed into during their session. /// /// public virtual async Task> GetClientListAsync() { await AuthenticateAsync(); if (Properties != null) { try { return Properties.GetClientList(); } catch (Exception ex) { Logger.LogError(ex, "Error decoding client list"); // clear so we don't keep failing Properties.RemoveClientList(); await UpdateSessionCookie(); } } return Enumerable.Empty(); } // client list helpers private async Task UpdateSessionCookie() { await AuthenticateAsync(); if (Principal == null || Properties == null) throw new InvalidOperationException("User is not currently authenticated"); var scheme = await HttpContext.GetCookieAuthenticationSchemeAsync(); await HttpContext.SignInAsync(scheme, Principal, Properties); } } ================================================ FILE: src/IdentityServer8/src/Services/Default/DistributedDeviceFlowThrottlingService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// The default device flow throttling service using IDistributedCache. /// /// public class DistributedDeviceFlowThrottlingService : IDeviceFlowThrottlingService { private readonly IDistributedCache _cache; private readonly ISystemClock _clock; private readonly IdentityServerOptions _options; private const string KeyPrefix = "devicecode_"; /// /// Initializes a new instance of the class. /// /// The cache. /// The clock. /// The options. public DistributedDeviceFlowThrottlingService( IDistributedCache cache, ISystemClock clock, IdentityServerOptions options) { _cache = cache; _clock = clock; _options = options; } /// /// Decides if the requesting client and device code needs to slow down. /// /// The device code. /// The device code details. /// /// deviceCode public async Task ShouldSlowDown(string deviceCode, DeviceCode details) { if (deviceCode == null) throw new ArgumentNullException(nameof(deviceCode)); var key = KeyPrefix + deviceCode; var options = new DistributedCacheEntryOptions {AbsoluteExpiration = _clock.UtcNow.AddSeconds(details.Lifetime)}; var lastSeenAsString = await _cache.GetStringAsync(key); // record new if (lastSeenAsString == null) { await _cache.SetStringAsync(key, _clock.UtcNow.ToString("O"), options); return false; } // check interval if (DateTime.TryParse(lastSeenAsString, out var lastSeen)) { if (_clock.UtcNow < lastSeen.AddSeconds(_options.DeviceFlow.Interval)) { await _cache.SetStringAsync(key, _clock.UtcNow.ToString("O"), options); return true; } } // store current and continue await _cache.SetStringAsync(key, _clock.UtcNow.ToString("O"), options); return false; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/LogoutNotificationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Default implementation of logout notification service. /// public class LogoutNotificationService : ILogoutNotificationService { private readonly IClientStore _clientStore; private readonly IHttpContextAccessor _httpContextAccessor; private readonly ILogger _logger; /// /// Ctor. /// public LogoutNotificationService( IClientStore clientStore, IHttpContextAccessor httpContextAccessor, ILogger logger) { _clientStore = clientStore; _httpContextAccessor = httpContextAccessor; _logger = logger; } /// public async Task> GetFrontChannelLogoutNotificationsUrlsAsync(LogoutNotificationContext context) { var frontChannelUrls = new List(); foreach (var clientId in context.ClientIds) { var client = await _clientStore.FindEnabledClientByIdAsync(clientId); if (client != null) { if (client.FrontChannelLogoutUri.IsPresent()) { var url = client.FrontChannelLogoutUri; // add session id if required if (client.ProtocolType == IdentityServerConstants.ProtocolTypes.OpenIdConnect) { if (client.FrontChannelLogoutSessionRequired) { url = url.AddQueryString(OidcConstants.EndSessionRequest.Sid, context.SessionId); url = url.AddQueryString(OidcConstants.EndSessionRequest.Issuer, _httpContextAccessor.HttpContext.GetIdentityServerIssuerUri()); } } else if (client.ProtocolType == IdentityServerConstants.ProtocolTypes.WsFederation) { url = url.AddQueryString(Constants.WsFedSignOut.LogoutUriParameterName, Constants.WsFedSignOut.LogoutUriParameterValue); } frontChannelUrls.Add(url); } } } if (frontChannelUrls.Any()) { var msg = frontChannelUrls.Aggregate((x, y) => x + ", " + y); _logger.LogDebug("Client front-channel logout URLs: {0}", Ioc.Sanitizer.Log.Sanitize(msg)); } else { _logger.LogDebug("No client front-channel logout URLs"); } return frontChannelUrls; } /// public async Task> GetBackChannelLogoutNotificationsAsync(LogoutNotificationContext context) { var backChannelLogouts = new List(); foreach (var clientId in context.ClientIds) { var client = await _clientStore.FindEnabledClientByIdAsync(clientId); if (client != null) { if (client.BackChannelLogoutUri.IsPresent()) { var back = new BackChannelLogoutRequest { ClientId = clientId, LogoutUri = client.BackChannelLogoutUri, SubjectId = context.SubjectId, SessionId = context.SessionId, SessionIdRequired = client.BackChannelLogoutSessionRequired }; backChannelLogouts.Add(back); } } } if (backChannelLogouts.Any()) { var msg = backChannelLogouts.Select(x => x.LogoutUri).Aggregate((x, y) => x + ", " + y); _logger.LogDebug("Client back-channel logout URLs: {0}", msg); } else { _logger.LogDebug("No client back-channel logout URLs"); } return backChannelLogouts; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/NumericUserCodeGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// User code generator using 9 digit number /// /// public class NumericUserCodeGenerator : IUserCodeGenerator { /// /// Gets the type of the user code. /// /// /// The type of the user code. /// public string UserCodeType => IdentityServerConstants.UserCodeTypes.Numeric; /// /// Gets the retry limit. /// /// /// The retry limit for getting a unique value. /// public int RetryLimit => 5; /// /// Generates the user code. /// /// public Task GenerateAsync() { var next = Next(100000000, 999999999); return Task.FromResult(next.ToString()); } private int Next(int minValue, int maxValue) { if (minValue > maxValue) throw new ArgumentOutOfRangeException(nameof(minValue)); if (minValue == maxValue) return minValue; long diff = maxValue - minValue; var uint32Buffer = new byte[8]; using (var rng = new RNGCryptoServiceProvider()) { while (true) { rng.GetBytes(uint32Buffer); var rand = BitConverter.ToUInt32(uint32Buffer, 0); const long max = 1 + (long)uint.MaxValue; var remainder = max % diff; if (rand < max - remainder) { return (int)(minValue + rand % diff); } } } } } ================================================ FILE: src/IdentityServer8/src/Services/Default/OidcReturnUrlParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; internal class OidcReturnUrlParser : IReturnUrlParser { private readonly IAuthorizeRequestValidator _validator; private readonly IUserSession _userSession; private readonly ILogger _logger; private readonly IAuthorizationParametersMessageStore _authorizationParametersMessageStore; public OidcReturnUrlParser( IAuthorizeRequestValidator validator, IUserSession userSession, ILogger logger, IAuthorizationParametersMessageStore authorizationParametersMessageStore = null) { _validator = validator; _userSession = userSession; _logger = logger; _authorizationParametersMessageStore = authorizationParametersMessageStore; } public async Task ParseAsync(string returnUrl) { if (IsValidReturnUrl(returnUrl)) { var parameters = returnUrl.ReadQueryStringAsNameValueCollection(); if (_authorizationParametersMessageStore != null) { var messageStoreId = parameters[Constants.AuthorizationParamsStore.MessageStoreIdParameterName]; var entry = await _authorizationParametersMessageStore.ReadAsync(messageStoreId); parameters = entry?.Data.FromFullDictionary() ?? new NameValueCollection(); } var user = await _userSession.GetUserAsync(); var result = await _validator.ValidateAsync(parameters, user); if (!result.IsError) { _logger.LogTrace("AuthorizationRequest being returned"); return new AuthorizationRequest(result.ValidatedRequest); } } _logger.LogTrace("No AuthorizationRequest being returned"); return null; } public bool IsValidReturnUrl(string returnUrl) { if (returnUrl.IsLocalUrl()) { var index = returnUrl.IndexOf('?'); if (index >= 0) { returnUrl = returnUrl.Substring(0, index); } if (returnUrl.EndsWith(Constants.ProtocolRoutePaths.Authorize, StringComparison.Ordinal) || returnUrl.EndsWith(Constants.ProtocolRoutePaths.AuthorizeCallback, StringComparison.Ordinal)) { _logger.LogTrace("returnUrl is valid"); return true; } } _logger.LogTrace("returnUrl is not valid"); return false; } } ================================================ FILE: src/IdentityServer8/src/Services/Default/ReturnUrlParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Parses a return URL using all registered URL parsers /// public class ReturnUrlParser { private readonly IEnumerable _parsers; /// /// Initializes a new instance of the class. /// /// The parsers. public ReturnUrlParser(IEnumerable parsers) { _parsers = parsers; } /// /// Parses the return URL. /// /// The return URL. /// public virtual async Task ParseAsync(string returnUrl) { foreach (var parser in _parsers) { var result = await parser.ParseAsync(returnUrl); if (result != null) { return result; } } return null; } /// /// Determines whether a return URL is valid. /// /// The return URL. /// /// true if the return URL is valid; otherwise, false. /// public virtual bool IsValidReturnUrl(string returnUrl) { foreach (var parser in _parsers) { if (parser.IsValidReturnUrl(returnUrl)) { return true; } } return false; } } ================================================ FILE: src/IdentityServer8/src/Services/IBackChannelLogoutHttpClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Models making HTTP requests for back-channel logout notification. /// public interface IBackChannelLogoutHttpClient { /// /// Performs HTTP POST. /// /// /// /// Task PostAsync(string url, Dictionary payload); } ================================================ FILE: src/IdentityServer8/src/Services/IBackChannelLogoutService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// The service responsible for performing back-channel logout notification. /// public interface IBackChannelLogoutService { /// /// Performs http back-channel logout notification. /// /// The context of the back channel logout notification. Task SendLogoutNotificationsAsync(LogoutNotificationContext context); } ================================================ FILE: src/IdentityServer8/src/Services/ICache.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Abstract interface to model data caching /// /// The data type to be cached public interface ICache where T : class { /// /// Gets the cached data based upon a key index. /// /// The key. /// The cached item, or null if no item matches the key. Task GetAsync(string key); /// /// Caches the data based upon a key /// /// The key. /// The item. /// The expiration. /// Task SetAsync(string key, T item, TimeSpan expiration); } ================================================ FILE: src/IdentityServer8/src/Services/IClaimsService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// The claims service is responsible for determining which claims to include in tokens /// public interface IClaimsService { /// /// Returns claims for an identity token /// /// The subject /// The resources. /// Specifies if all claims should be included in the token, or if the userinfo endpoint can be used to retrieve them /// The raw request /// /// Claims for the identity token /// Task> GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resources, bool includeAllIdentityClaims, ValidatedRequest request); /// /// Returns claims for an access token. /// /// The subject. /// The resources. /// The raw request. /// /// Claims for the access token /// Task> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resources, ValidatedRequest request); } ================================================ FILE: src/IdentityServer8/src/Services/IConsentService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Service to retrieve and update consent. /// public interface IConsentService { /// /// Checks if consent is required. /// /// The user. /// The client. /// The parsed scopes. /// /// Boolean if consent is required. /// Task RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable parsedScopes); /// /// Updates the consent. /// /// The subject. /// The client. /// The parsed scopes. /// Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable parsedScopes); } ================================================ FILE: src/IdentityServer8/src/Services/IDeviceFlowCodeService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Wrapper service for IDeviceFlowStore. /// public interface IDeviceFlowCodeService { /// /// Stores the device authorization request. /// /// The user code. /// The data. Task StoreDeviceAuthorizationAsync(string userCode, DeviceCode data); /// /// Finds device authorization by user code. /// /// The user code. /// Task FindByUserCodeAsync(string userCode); /// /// Finds device authorization by device code. /// /// The device code. Task FindByDeviceCodeAsync(string deviceCode); /// /// Updates device authorization, searching by user code. /// /// The user code. /// The data. Task UpdateByUserCodeAsync(string userCode, DeviceCode data); /// /// Removes the device authorization, searching by device code. /// /// The device code. Task RemoveByDeviceCodeAsync(string deviceCode); } ================================================ FILE: src/IdentityServer8/src/Services/IDeviceFlowInteractionService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Provide services be used by the user interface to communicate with IdentityServer. /// public interface IDeviceFlowInteractionService { /// /// Gets the authorization context asynchronous. /// /// The user code. /// Task GetAuthorizationContextAsync(string userCode); /// /// Handles the request asynchronous. /// /// The user code. /// The consent. /// Task HandleRequestAsync(string userCode, ConsentResponse consent); } ================================================ FILE: src/IdentityServer8/src/Services/IDeviceFlowThrottlingService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// The device flow throttling service. /// public interface IDeviceFlowThrottlingService { /// /// Decides if the requesting client and device code needs to slow down. /// /// The device code. /// The device code details. /// Task ShouldSlowDown(string deviceCode, DeviceCode details); } ================================================ FILE: src/IdentityServer8/src/Services/IEventService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Interface for the event service /// public interface IEventService { /// /// Raises the specified event. /// /// The event. Task RaiseAsync(Event evt); /// /// Indicates if the type of event will be persisted. /// bool CanRaiseEventType(EventTypes evtType); } ================================================ FILE: src/IdentityServer8/src/Services/IEventSink.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Models persistence of events /// public interface IEventSink { /// /// Raises the specified event. /// /// The event. Task PersistAsync(Event evt); } ================================================ FILE: src/IdentityServer8/src/Services/IHandleGenerationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Interface for the handle generation service /// public interface IHandleGenerationService { /// /// Generates a handle. /// /// The length. /// Task GenerateAsync(int length = 32); } ================================================ FILE: src/IdentityServer8/src/Services/IIdentityServerInteractionService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Provide services be used by the user interface to communicate with IdentityServer. /// public interface IIdentityServerInteractionService { /// /// Gets the authorization context. /// /// The return URL. Task GetAuthorizationContextAsync(string returnUrl); /// /// Indicates if the returnUrl is a valid URL for redirect after login or consent. /// /// The return URL. bool IsValidReturnUrl(string returnUrl); /// /// Gets the error context. /// /// The error identifier. Task GetErrorContextAsync(string errorId); /// /// Gets the logout context. /// /// The logout identifier. Task GetLogoutContextAsync(string logoutId); /// /// Used to create a logoutId if there is not one presently. /// /// Task CreateLogoutContextAsync(); /// /// Informs IdentityServer of the user's consent. /// /// The request. /// The consent. /// The subject. Task GrantConsentAsync(AuthorizationRequest request, ConsentResponse consent, string subject = null); /// /// Triggers error back to the client for the authorization request. /// This API is a simpler helper on top of GrantConsentAsync. /// /// The request. /// /// Task DenyAuthorizationAsync(AuthorizationRequest request, AuthorizationError error, string errorDescription = null); /// /// Returns a collection representing all of the user's consents and grants. /// Task> GetAllUserGrantsAsync(); /// /// Revokes all a user's consents and grants for a client. /// /// The client identifier. Task RevokeUserConsentAsync(string clientId); /// /// Revokes all of a user's consents and grants for clients the user has signed into during their current session. /// Task RevokeTokensForCurrentSessionAsync(); } ================================================ FILE: src/IdentityServer8/src/Services/IJwtRequestUriHttpClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Models making HTTP requests for JWTs from the authorize endpoint. /// public interface IJwtRequestUriHttpClient { /// /// Gets a JWT from the url. /// /// /// /// Task GetJwtAsync(string url, Client client); } ================================================ FILE: src/IdentityServer8/src/Services/IKeyMaterialService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Services; /// /// Interface for the key material service /// public interface IKeyMaterialService { /// /// Gets all validation keys. /// /// Task> GetValidationKeysAsync(); /// /// Gets the signing credentials. /// /// Collection of algorithms used to filter the server supported algorithms. /// A value of null or empty indicates that the server default should be returned. /// Task GetSigningCredentialsAsync(IEnumerable allowedAlgorithms = null); /// /// Gets all signing credentials. /// /// Task> GetAllSigningCredentialsAsync(); } ================================================ FILE: src/IdentityServer8/src/Services/ILogoutNotificationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Provides features for OIDC signout notifications. /// public interface ILogoutNotificationService { /// /// Builds the URLs needed for front-channel logout notification. /// /// The context for the logout notification. Task> GetFrontChannelLogoutNotificationsUrlsAsync(LogoutNotificationContext context); /// /// Builds the http back-channel logout request data for the collection of clients. /// /// The context for the logout notification. Task> GetBackChannelLogoutNotificationsAsync(LogoutNotificationContext context); } /// /// Information necessary to make a back-channel logout request to a client. /// public class BackChannelLogoutRequest { /// /// Gets or sets the client identifier. /// public string ClientId { get; set; } /// /// Gets the subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the session identifier. /// public string SessionId { get; set; } /// /// Gets or sets the back channel logout URI. /// public string LogoutUri { get; set; } /// /// Gets a value indicating whether the session identifier is required. /// public bool SessionIdRequired { get; set; } } ================================================ FILE: src/IdentityServer8/src/Services/IPersistedGrantService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Implements persisted grant logic /// public interface IPersistedGrantService { /// /// Gets all grants for a given subject ID. /// /// The subject identifier. /// Task> GetAllGrantsAsync(string subjectId); /// /// Removes all grants for a given subject id, and optionally client id and session id combination. /// /// The subject identifier. /// The client identifier (optional). /// The sesion id (optional). /// Task RemoveAllGrantsAsync(string subjectId, string clientId = null, string sessionId = null); } ================================================ FILE: src/IdentityServer8/src/Services/IProfileService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// This interface allows IdentityServer to connect to your user and profile store. /// public interface IProfileService { /// /// This method is called whenever claims about the user are requested (e.g. during token creation or via the userinfo endpoint) /// /// The context. /// Task GetProfileDataAsync(ProfileDataRequestContext context); /// /// This method gets called whenever identity server needs to determine if the user is valid or active (e.g. if the user's account has been deactivated since they logged in). /// (e.g. during token issuance or validation). /// /// The context. /// Task IsActiveAsync(IsActiveContext context); } ================================================ FILE: src/IdentityServer8/src/Services/IRefreshTokenService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Implements refresh token creation and validation /// public interface IRefreshTokenService { /// /// Validates a refresh token. /// /// The refresh token. /// The client. /// Task ValidateRefreshTokenAsync(string token, Client client); /// /// Creates the refresh token. /// /// The subject. /// The access token. /// The client. /// /// The refresh token handle /// Task CreateRefreshTokenAsync(ClaimsPrincipal subject, Token accessToken, Client client); /// /// Updates the refresh token. /// /// The handle. /// The refresh token. /// The client. /// /// The refresh token handle /// Task UpdateRefreshTokenAsync(string handle, RefreshToken refreshToken, Client client); } ================================================ FILE: src/IdentityServer8/src/Services/IReplayCache.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Interface for replay cache implementations /// public interface IReplayCache { /// /// Adds a handle to the cache /// /// /// /// /// Task AddAsync(string purpose, string handle, DateTimeOffset expiration); /// /// Checks if a cached handle exists /// /// /// /// Task ExistsAsync(string purpose, string handle); } ================================================ FILE: src/IdentityServer8/src/Services/IReturnUrlParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Interface for the return URL parser /// public interface IReturnUrlParser { /// /// Parses a return URL. /// /// The return URL. /// Task ParseAsync(string returnUrl); /// /// Determines whether the return URL is valid. /// /// The return URL. /// /// true if the return URL is valid; otherwise, false. /// bool IsValidReturnUrl(string returnUrl); } ================================================ FILE: src/IdentityServer8/src/Services/ITokenCreationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Logic for creating security tokens /// public interface ITokenCreationService { /// /// Creates a token. /// /// The token description. /// A protected and serialized security token Task CreateTokenAsync(Token token); } ================================================ FILE: src/IdentityServer8/src/Services/ITokenService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Logic for creating security tokens /// public interface ITokenService { /// /// Creates an identity token. /// /// The token creation request. /// An identity token Task CreateIdentityTokenAsync(TokenCreationRequest request); /// /// Creates an access token. /// /// The token creation request. /// An access token Task CreateAccessTokenAsync(TokenCreationRequest request); /// /// Creates a serialized and protected security token. /// /// The token. /// A security token in serialized form Task CreateSecurityTokenAsync(Token token); } ================================================ FILE: src/IdentityServer8/src/Services/IUserCodeGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Implements device flow user code generation /// public interface IUserCodeGenerator { /// /// Gets the type of the user code. /// /// /// The type of the user code. /// string UserCodeType { get; } /// /// Gets the retry limit. /// /// /// The retry limit for getting a unique value. /// int RetryLimit { get; } /// /// Generates the user code. /// /// Task GenerateAsync(); } ================================================ FILE: src/IdentityServer8/src/Services/IUserCodeService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Implements user code generation /// public interface IUserCodeService { /// /// Gets the user code generator. /// /// Type of user code. /// Task GetGenerator(string userCodeType); } ================================================ FILE: src/IdentityServer8/src/Services/IUserSession.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Models a user's authentication session /// public interface IUserSession { /// /// Creates a session identifier for the signin context and issues the session id cookie. /// Task CreateSessionIdAsync(ClaimsPrincipal principal, AuthenticationProperties properties); /// /// Gets the current authenticated user. /// Task GetUserAsync(); /// /// Gets the current session identifier. /// /// Task GetSessionIdAsync(); /// /// Ensures the session identifier cookie asynchronous. /// /// Task EnsureSessionIdCookieAsync(); /// /// Removes the session identifier cookie. /// Task RemoveSessionIdCookieAsync(); /// /// Adds a client to the list of clients the user has signed into during their session. /// /// The client identifier. /// Task AddClientIdAsync(string clientId); /// /// Gets the list of clients the user has signed into during their session. /// /// Task> GetClientListAsync(); } ================================================ FILE: src/IdentityServer8/src/Services/InMemory/InMemoryCorsPolicyService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// CORS policy service that configures the allowed origins from a list of clients' redirect URLs. /// public class InMemoryCorsPolicyService : ICorsPolicyService { /// /// Logger /// protected readonly ILogger Logger; /// /// Clients applications list /// protected readonly IEnumerable Clients; /// /// Initializes a new instance of the class. /// /// The logger /// The clients. public InMemoryCorsPolicyService(ILogger logger, IEnumerable clients) { Logger = logger; Clients = clients ?? Enumerable.Empty(); } /// /// Determines whether origin is allowed. /// /// The origin. /// public virtual Task IsOriginAllowedAsync(string origin) { var query = from client in Clients from url in client.AllowedCorsOrigins select url.GetOrigin(); var result = query.Contains(origin, StringComparer.OrdinalIgnoreCase); if (result) { Logger.LogDebug("Client list checked and origin: {0} is allowed", Ioc.Sanitizer.Log.Sanitize(origin)); } else { Logger.LogDebug("Client list checked and origin: {0} is not allowed", Ioc.Sanitizer.Log.Sanitize(origin)); } return Task.FromResult(result); } } ================================================ FILE: src/IdentityServer8/src/Stores/Caching/CachingClientStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Cache decorator for IClientStore /// /// /// public class CachingClientStore : IClientStore where T : IClientStore { private readonly IdentityServerOptions _options; private readonly ICache _cache; private readonly IClientStore _inner; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The options. /// The inner. /// The cache. /// The logger. public CachingClientStore(IdentityServerOptions options, T inner, ICache cache, ILogger> logger) { _options = options; _inner = inner; _cache = cache; _logger = logger; } /// /// Finds a client by id /// /// The client id /// /// The client /// public async Task FindClientByIdAsync(string clientId) { var client = await _cache.GetAsync(clientId, _options.Caching.ClientStoreExpiration, async () => await _inner.FindClientByIdAsync(clientId), _logger); return client; } } ================================================ FILE: src/IdentityServer8/src/Stores/Caching/CachingCorsPolicyService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Caching decorator for ICorsPolicyService /// /// public class CachingCorsPolicyService : ICorsPolicyService where T : ICorsPolicyService { /// /// Class to model entries in CORS origin cache. /// public class CorsCacheEntry { /// /// Ctor. /// public CorsCacheEntry(bool allowed) { Allowed = allowed; } /// /// Is origin allowed. /// public bool Allowed { get; } } private readonly IdentityServerOptions Options; private readonly ICache CorsCache; private readonly ICorsPolicyService Inner; private readonly ILogger Logger; /// /// Initializes a new instance of the class. /// /// The options. /// The inner. /// The CORS origin cache. /// The logger. public CachingCorsPolicyService(IdentityServerOptions options, T inner, ICache corsCache, ILogger> logger) { Options = options; Inner = inner; CorsCache = corsCache; Logger = logger; } /// /// Determines whether origin is allowed. /// /// The origin. /// public virtual async Task IsOriginAllowedAsync(string origin) { var entry = await CorsCache.GetAsync(origin, Options.Caching.CorsExpiration, async () => new CorsCacheEntry(await Inner.IsOriginAllowedAsync(origin)), Logger); return entry.Allowed; } } ================================================ FILE: src/IdentityServer8/src/Stores/Caching/CachingResourceStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Caching decorator for IResourceStore /// /// /// public class CachingResourceStore : IResourceStore where T : IResourceStore { private const string AllKey = "__all__"; private readonly IdentityServerOptions _options; private readonly ICache> _identityCache; private readonly ICache> _apiByScopeCache; private readonly ICache> _apiScopeCache; private readonly ICache> _apiResourceCache; private readonly ICache _allCache; private readonly IResourceStore _inner; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The options. /// The inner. /// The identity cache. /// The API by scope cache. /// The API cache. /// /// All cache. /// The logger. public CachingResourceStore(IdentityServerOptions options, T inner, ICache> identityCache, ICache> apiByScopeCache, ICache> apisCache, ICache> scopeCache, ICache allCache, ILogger> logger) { _options = options; _inner = inner; _identityCache = identityCache; _apiByScopeCache = apiByScopeCache; _apiResourceCache = apisCache; _apiScopeCache = scopeCache; _allCache = allCache; _logger = logger; } private string GetKey(IEnumerable names) { if (names == null || !names.Any()) return string.Empty; return names.OrderBy(x => x).Aggregate((x, y) => x + "," + y); } /// public async Task GetAllResourcesAsync() { var key = AllKey; var all = await _allCache.GetAsync(key, _options.Caching.ResourceStoreExpiration, async () => await _inner.GetAllResourcesAsync(), _logger); return all; } /// public async Task> FindApiResourcesByNameAsync(IEnumerable apiResourceNames) { var key = GetKey(apiResourceNames); var apis = await _apiResourceCache.GetAsync(key, _options.Caching.ResourceStoreExpiration, async () => await _inner.FindApiResourcesByNameAsync(apiResourceNames), _logger); return apis; } /// public async Task> FindIdentityResourcesByScopeNameAsync(IEnumerable names) { var key = GetKey(names); var identities = await _identityCache.GetAsync(key, _options.Caching.ResourceStoreExpiration, async () => await _inner.FindIdentityResourcesByScopeNameAsync(names), _logger); return identities; } /// public async Task> FindApiResourcesByScopeNameAsync(IEnumerable names) { var key = GetKey(names); var apis = await _apiByScopeCache.GetAsync(key, _options.Caching.ResourceStoreExpiration, async () => await _inner.FindApiResourcesByScopeNameAsync(names), _logger); return apis; } /// public async Task> FindApiScopesByNameAsync(IEnumerable scopeNames) { var key = GetKey(scopeNames); var apis = await _apiScopeCache.GetAsync(key, _options.Caching.ResourceStoreExpiration, async () => await _inner.FindApiScopesByNameAsync(scopeNames), _logger); return apis; } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/ConsentMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; internal class ConsentMessageStore : IConsentMessageStore { protected readonly MessageCookie Cookie; public ConsentMessageStore(MessageCookie cookie) { Cookie = cookie; } public virtual Task DeleteAsync(string id) { Cookie.Clear(id); return Task.CompletedTask; } public virtual Task> ReadAsync(string id) { return Task.FromResult(Cookie.Read(id)); } public virtual Task WriteAsync(string id, Message message) { Cookie.Write(id, message); return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/DefaultAuthorizationCodeStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Default authorization code store. /// public class DefaultAuthorizationCodeStore : DefaultGrantStore, IAuthorizationCodeStore { /// /// Initializes a new instance of the class. /// /// The store. /// The serializer. /// The handle generation service. /// The logger. public DefaultAuthorizationCodeStore( IPersistedGrantStore store, IPersistentGrantSerializer serializer, IHandleGenerationService handleGenerationService, ILogger logger) : base(IdentityServerConstants.PersistedGrantTypes.AuthorizationCode, store, serializer, handleGenerationService, logger) { } /// /// Stores the authorization code asynchronous. /// /// The code. /// public Task StoreAuthorizationCodeAsync(AuthorizationCode code) { return CreateItemAsync(code, code.ClientId, code.Subject.GetSubjectId(), code.SessionId, code.Description, code.CreationTime, code.Lifetime); } /// /// Gets the authorization code asynchronous. /// /// The code. /// public Task GetAuthorizationCodeAsync(string code) { return GetItemAsync(code); } /// /// Removes the authorization code asynchronous. /// /// The code. /// public Task RemoveAuthorizationCodeAsync(string code) { return RemoveItemAsync(code); } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/DefaultGrantStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Base class for persisting grants using the IPersistedGrantStore. /// /// public class DefaultGrantStore { /// /// The grant type being stored. /// protected string GrantType { get; } /// /// The logger. /// protected ILogger Logger { get; } /// /// The PersistedGrantStore. /// protected IPersistedGrantStore Store { get; } /// /// The PersistentGrantSerializer; /// protected IPersistentGrantSerializer Serializer { get; } /// /// The HandleGenerationService. /// protected IHandleGenerationService HandleGenerationService { get; } /// /// Initializes a new instance of the class. /// /// Type of the grant. /// The store. /// The serializer. /// The handle generation service. /// The logger. /// grantType protected DefaultGrantStore(string grantType, IPersistedGrantStore store, IPersistentGrantSerializer serializer, IHandleGenerationService handleGenerationService, ILogger logger) { if (grantType.IsMissing()) throw new ArgumentNullException(nameof(grantType)); GrantType = grantType; Store = store; Serializer = serializer; HandleGenerationService = handleGenerationService; Logger = logger; } private const string KeySeparator = ":"; /// /// Gets the hashed key. /// /// The value. /// protected virtual string GetHashedKey(string value) { return (value + KeySeparator + GrantType).Sha256(); } /// /// Gets the item. /// /// The key. /// protected virtual async Task GetItemAsync(string key) { var hashedKey = GetHashedKey(key); var grant = await Store.GetAsync(hashedKey); if (grant != null && grant.Type == GrantType) { try { return Serializer.Deserialize(grant.Data); } catch (Exception ex) { Logger.LogError(ex, "Failed to deserialize JSON from grant store."); } } else { Logger.LogDebug("{grantType} grant with value: {key} not found in store.", GrantType, Ioc.Sanitizer.Log.Sanitize(key)); } return default; } /// /// Creates the item. /// /// The item. /// The client identifier. /// The subject identifier. /// The session identifier. /// The description. /// The created. /// The lifetime. /// protected virtual async Task CreateItemAsync(T item, string clientId, string subjectId, string sessionId, string description, DateTime created, int lifetime) { var handle = await HandleGenerationService.GenerateAsync(); await StoreItemAsync(handle, item, clientId, subjectId, sessionId, description, created, created.AddSeconds(lifetime)); return handle; } /// /// Stores the item. /// /// The key. /// The item. /// The client identifier. /// The subject identifier. /// The session identifier. /// The description. /// The created time. /// The expiration. /// The consumed time. /// protected virtual async Task StoreItemAsync(string key, T item, string clientId, string subjectId, string sessionId, string description, DateTime created, DateTime? expiration, DateTime? consumedTime = null) { key = GetHashedKey(key); var json = Serializer.Serialize(item); var grant = new PersistedGrant { Key = key, Type = GrantType, ClientId = clientId, SubjectId = subjectId, SessionId = sessionId, Description = description, CreationTime = created, Expiration = expiration, ConsumedTime = consumedTime, Data = json }; await Store.StoreAsync(grant); } /// /// Removes the item. /// /// The key. /// protected virtual async Task RemoveItemAsync(string key) { key = GetHashedKey(key); await Store.RemoveAsync(key); } /// /// Removes all items for a subject id / cliend id combination. /// /// The subject identifier. /// The client identifier. /// protected virtual async Task RemoveAllAsync(string subjectId, string clientId) { await Store.RemoveAllAsync(new PersistedGrantFilter { SubjectId = subjectId, ClientId = clientId, Type = GrantType }); } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/DefaultReferenceTokenStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Default reference token store. /// public class DefaultReferenceTokenStore : DefaultGrantStore, IReferenceTokenStore { /// /// Initializes a new instance of the class. /// /// The store. /// The serializer. /// The handle generation service. /// The logger. public DefaultReferenceTokenStore( IPersistedGrantStore store, IPersistentGrantSerializer serializer, IHandleGenerationService handleGenerationService, ILogger logger) : base(IdentityServerConstants.PersistedGrantTypes.ReferenceToken, store, serializer, handleGenerationService, logger) { } /// /// Stores the reference token asynchronous. /// /// The token. /// public Task StoreReferenceTokenAsync(Token token) { return CreateItemAsync(token, token.ClientId, token.SubjectId, token.SessionId, token.Description, token.CreationTime, token.Lifetime); } /// /// Gets the reference token asynchronous. /// /// The handle. /// public Task GetReferenceTokenAsync(string handle) { return GetItemAsync(handle); } /// /// Removes the reference token asynchronous. /// /// The handle. /// public Task RemoveReferenceTokenAsync(string handle) { return RemoveItemAsync(handle); } /// /// Removes the reference tokens asynchronous. /// /// The subject identifier. /// The client identifier. /// public Task RemoveReferenceTokensAsync(string subjectId, string clientId) { return RemoveAllAsync(subjectId, clientId); } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/DefaultRefreshTokenStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Default refresh token store. /// public class DefaultRefreshTokenStore : DefaultGrantStore, IRefreshTokenStore { /// /// Initializes a new instance of the class. /// /// The store. /// The serializer. /// The handle generation service. /// The logger. public DefaultRefreshTokenStore( IPersistedGrantStore store, IPersistentGrantSerializer serializer, IHandleGenerationService handleGenerationService, ILogger logger) : base(IdentityServerConstants.PersistedGrantTypes.RefreshToken, store, serializer, handleGenerationService, logger) { } /// /// Stores the refresh token. /// /// The refresh token. /// public async Task StoreRefreshTokenAsync(RefreshToken refreshToken) { return await CreateItemAsync(refreshToken, refreshToken.ClientId, refreshToken.SubjectId, refreshToken.SessionId, refreshToken.Description, refreshToken.CreationTime, refreshToken.Lifetime); } /// /// Updates the refresh token. /// /// The handle. /// The refresh token. /// public Task UpdateRefreshTokenAsync(string handle, RefreshToken refreshToken) { return StoreItemAsync(handle, refreshToken, refreshToken.ClientId, refreshToken.SubjectId, refreshToken.SessionId, refreshToken.Description, refreshToken.CreationTime, refreshToken.CreationTime.AddSeconds(refreshToken.Lifetime), refreshToken.ConsumedTime); } /// /// Gets the refresh token. /// /// The refresh token handle. /// public Task GetRefreshTokenAsync(string refreshTokenHandle) { return GetItemAsync(refreshTokenHandle); } /// /// Removes the refresh token. /// /// The refresh token handle. /// public Task RemoveRefreshTokenAsync(string refreshTokenHandle) { return RemoveItemAsync(refreshTokenHandle); } /// /// Removes the refresh tokens. /// /// The subject identifier. /// The client identifier. /// public Task RemoveRefreshTokensAsync(string subjectId, string clientId) { return RemoveAllAsync(subjectId, clientId); } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/DefaultUserConsentStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Default user consent store. /// public class DefaultUserConsentStore : DefaultGrantStore, IUserConsentStore { /// /// Initializes a new instance of the class. /// /// The store. /// The serializer. /// The handle generation service. /// The logger. public DefaultUserConsentStore( IPersistedGrantStore store, IPersistentGrantSerializer serializer, IHandleGenerationService handleGenerationService, ILogger logger) : base(IdentityServerConstants.PersistedGrantTypes.UserConsent, store, serializer, handleGenerationService, logger) { } private string GetConsentKey(string subjectId, string clientId) { return clientId + "|" + subjectId; } /// /// Stores the user consent asynchronous. /// /// The consent. /// public Task StoreUserConsentAsync(Consent consent) { var key = GetConsentKey(consent.SubjectId, consent.ClientId); return StoreItemAsync(key, consent, consent.ClientId, consent.SubjectId, null, null, consent.CreationTime, consent.Expiration); } /// /// Gets the user consent asynchronous. /// /// The subject identifier. /// The client identifier. /// public Task GetUserConsentAsync(string subjectId, string clientId) { var key = GetConsentKey(subjectId, clientId); return GetItemAsync(key); } /// /// Removes the user consent asynchronous. /// /// The subject identifier. /// The client identifier. /// public Task RemoveUserConsentAsync(string subjectId, string clientId) { var key = GetConsentKey(subjectId, clientId); return RemoveItemAsync(key); } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/DistributedCacheAuthorizationParametersMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores.Default; /// /// Implementation of IAuthorizationParametersMessageStore that uses the IDistributedCache. /// public class DistributedCacheAuthorizationParametersMessageStore : IAuthorizationParametersMessageStore { private readonly IDistributedCache _distributedCache; private readonly IHandleGenerationService _handleGenerationService; /// /// Ctor. /// /// /// public DistributedCacheAuthorizationParametersMessageStore(IDistributedCache distributedCache, IHandleGenerationService handleGenerationService) { _distributedCache = distributedCache; _handleGenerationService = handleGenerationService; } private string CacheKeyPrefix => "DistributedCacheAuthorizationParametersMessageStore"; /// public async Task WriteAsync(Message> message) { // since this store is trusted and the JWT request processing has provided redundant entries // in the NameValueCollection, we are removing the JWT "request_uri" param so that when they // are reloaded/revalidated we don't re-trigger outbound requests. we could possibly do the // same for the "request" param, but it's less of a concern (as it's just a signature check). message.Data.Remove(OidcConstants.AuthorizeRequest.RequestUri); var key = await _handleGenerationService.GenerateAsync(); var cacheKey = $"{CacheKeyPrefix}-{key}"; var json = ObjectSerializer.ToString(message); var options = new DistributedCacheEntryOptions(); options.SetSlidingExpiration(Constants.DefaultCacheDuration); await _distributedCache.SetStringAsync(cacheKey, json, options); return key; } /// public async Task>> ReadAsync(string id) { var cacheKey = $"{CacheKeyPrefix}-{id}"; var json = await _distributedCache.GetStringAsync(cacheKey); if (json == null) { return new Message>(new Dictionary()); } return ObjectSerializer.FromString>>(json); } /// public Task DeleteAsync(string id) { return _distributedCache.RemoveAsync(id); } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/ProtectedDataMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.DataProtection; namespace IdentityServer8.Stores; /// /// IMessageStore implementation that uses data protection to protect message. /// /// public class ProtectedDataMessageStore : IMessageStore { private const string Purpose = "IdentityServer8.Stores.ProtectedDataMessageStore"; /// /// The data protector. /// protected readonly IDataProtector Protector; /// /// The logger. /// protected readonly ILogger Logger; /// /// Ctor /// /// /// public ProtectedDataMessageStore(IDataProtectionProvider provider, ILogger> logger) { Protector = provider.CreateProtector(Purpose); Logger = logger; } /// public virtual Task> ReadAsync(string value) { Message result = null; if (!String.IsNullOrWhiteSpace(value)) { try { var bytes = Base64Url.Decode(value); bytes = Protector.Unprotect(bytes); var json = Encoding.UTF8.GetString(bytes); result = ObjectSerializer.FromString>(json); } catch(Exception ex) { Logger.LogError(ex, "Exception reading protected message"); } } return Task.FromResult(result); } /// public virtual Task WriteAsync(Message message) { string value = null; try { var json = ObjectSerializer.ToString(message); var bytes = Encoding.UTF8.GetBytes(json); bytes = Protector.Protect(bytes); value = Base64Url.Encode(bytes); } catch(Exception ex) { Logger.LogError(ex, "Exception writing protected message"); } return Task.FromResult(value); } } ================================================ FILE: src/IdentityServer8/src/Stores/Default/QueryStringAuthorizationParametersMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; // internal just for testing internal class QueryStringAuthorizationParametersMessageStore : IAuthorizationParametersMessageStore { public Task WriteAsync(Message> message) { var queryString = message.Data.FromFullDictionary().ToQueryString(); return Task.FromResult(queryString); } public Task>> ReadAsync(string id) { var values = id.ReadQueryStringAsNameValueCollection(); var msg = new Message>(values.ToFullDictionary()); return Task.FromResult(msg); } public Task DeleteAsync(string id) { return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Stores/IAuthorizationParametersMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for authorization request messages that are sent from the authorization endpoint to the login and consent UI. /// public interface IAuthorizationParametersMessageStore { /// /// Writes the authorization parameters. /// /// The message. /// The identifier for the stored message. Task WriteAsync(Message> message); /// /// Reads the authorization parameters. /// /// The identifier. /// Task>> ReadAsync(string id); /// /// Deletes the authorization parameters. /// /// The identifier. /// Task DeleteAsync(string id); } ================================================ FILE: src/IdentityServer8/src/Stores/IConsentMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for consent messages that are sent from the consent UI to the authorization endpoint. /// public interface IConsentMessageStore { /// /// Writes the consent response message. /// /// The id for the message. /// The message. Task WriteAsync(string id, Message message); /// /// Reads the consent response message. /// /// The identifier. /// Task> ReadAsync(string id); /// /// Deletes the consent response message. /// /// The identifier. /// Task DeleteAsync(string id); } ================================================ FILE: src/IdentityServer8/src/Stores/IMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for a message store /// /// The type of the model. public interface IMessageStore { /// /// Writes the message. /// /// The message. /// An identifier for the message Task WriteAsync(Message message); /// /// Reads the message. /// /// The identifier. /// Task> ReadAsync(string id); } ================================================ FILE: src/IdentityServer8/src/Stores/ISigningCredentialStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Stores; /// /// Interface for a signing credential store /// public interface ISigningCredentialStore { /// /// Gets the signing credentials. /// /// Task GetSigningCredentialsAsync(); } ================================================ FILE: src/IdentityServer8/src/Stores/IValidationKeysStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for the validation key store /// public interface IValidationKeysStore { /// /// Gets all validation keys. /// /// Task> GetValidationKeysAsync(); } ================================================ FILE: src/IdentityServer8/src/Stores/InMemory/InMemoryClientStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// In-memory client store /// public class InMemoryClientStore : IClientStore { private readonly IEnumerable _clients; /// /// Initializes a new instance of the class. /// /// The clients. public InMemoryClientStore(IEnumerable clients) { if (clients.HasDuplicates(m => m.ClientId)) { throw new ArgumentException("Clients must not contain duplicate ids"); } _clients = clients; } /// /// Finds a client by id /// /// The client id /// /// The client /// public Task FindClientByIdAsync(string clientId) { var query = from client in _clients where client.ClientId == clientId select client; return Task.FromResult(query.SingleOrDefault()); } } ================================================ FILE: src/IdentityServer8/src/Stores/InMemory/InMemoryDeviceFlowStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// In-memory device flow store /// /// public class InMemoryDeviceFlowStore : IDeviceFlowStore { private readonly List _repository = new List(); /// /// Stores the device authorization request. /// /// The device code. /// The user code. /// The data. /// public Task StoreDeviceAuthorizationAsync(string deviceCode, string userCode, DeviceCode data) { lock (_repository) { _repository.Add(new InMemoryDeviceAuthorization(deviceCode, userCode, data)); } return Task.CompletedTask; } /// /// Finds device authorization by user code. /// /// The user code. public Task FindByUserCodeAsync(string userCode) { DeviceCode foundDeviceCode; lock (_repository) { foundDeviceCode = _repository.FirstOrDefault(x => x.UserCode == userCode)?.Data; } return Task.FromResult(foundDeviceCode); } /// /// Finds device authorization by device code. /// /// The device code. public Task FindByDeviceCodeAsync(string deviceCode) { DeviceCode foundDeviceCode; lock (_repository) { foundDeviceCode = _repository.FirstOrDefault(x => x.DeviceCode == deviceCode)?.Data; } return Task.FromResult(foundDeviceCode); } /// /// Updates device authorization, searching by user code. /// /// The user code. /// The data. public Task UpdateByUserCodeAsync(string userCode, DeviceCode data) { lock (_repository) { var foundData = _repository.FirstOrDefault(x => x.UserCode == userCode); if (foundData != null) { foundData.Data = data; } } return Task.CompletedTask; } /// /// Removes the device authorization, searching by device code. /// /// The device code. /// public Task RemoveByDeviceCodeAsync(string deviceCode) { lock (_repository) { var foundData = _repository.FirstOrDefault(x => x.DeviceCode == deviceCode); if (foundData != null) { _repository.Remove(foundData); } } return Task.CompletedTask; } private class InMemoryDeviceAuthorization { public InMemoryDeviceAuthorization(string deviceCode, string userCode, DeviceCode data) { DeviceCode = deviceCode; UserCode = userCode; Data = data; } public string DeviceCode { get; } public string UserCode { get; } public DeviceCode Data { get; set; } } } ================================================ FILE: src/IdentityServer8/src/Stores/InMemory/InMemoryPersistedGrantStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// In-memory persisted grant store /// public class InMemoryPersistedGrantStore : IPersistedGrantStore { private readonly ConcurrentDictionary _repository = new ConcurrentDictionary(); /// public Task StoreAsync(PersistedGrant grant) { _repository[grant.Key] = grant; return Task.CompletedTask; } /// public Task GetAsync(string key) { if (_repository.TryGetValue(key, out PersistedGrant token)) { return Task.FromResult(token); } return Task.FromResult(null); } /// public Task> GetAllAsync(PersistedGrantFilter filter) { filter.Validate(); var items = Filter(filter); return Task.FromResult(items); } /// public Task RemoveAsync(string key) { _repository.TryRemove(key, out _); return Task.CompletedTask; } /// public Task RemoveAllAsync(PersistedGrantFilter filter) { filter.Validate(); var items = Filter(filter); foreach (var item in items) { _repository.TryRemove(item.Key, out _); } return Task.CompletedTask; } private IEnumerable Filter(PersistedGrantFilter filter) { var query = from item in _repository select item.Value; if (!String.IsNullOrWhiteSpace(filter.ClientId)) { query = query.Where(x => x.ClientId == filter.ClientId); } if (!String.IsNullOrWhiteSpace(filter.SessionId)) { query = query.Where(x => x.SessionId == filter.SessionId); } if (!String.IsNullOrWhiteSpace(filter.SubjectId)) { query = query.Where(x => x.SubjectId == filter.SubjectId); } if (!String.IsNullOrWhiteSpace(filter.Type)) { query = query.Where(x => x.Type == filter.Type); } var items = query.ToArray().AsEnumerable(); return items; } } ================================================ FILE: src/IdentityServer8/src/Stores/InMemory/InMemoryResourcesStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// In-memory resource store /// public class InMemoryResourcesStore : IResourceStore { private readonly IEnumerable _identityResources; private readonly IEnumerable _apiResources; private readonly IEnumerable _apiScopes; /// /// Initializes a new instance of the class. /// public InMemoryResourcesStore( IEnumerable identityResources = null, IEnumerable apiResources = null, IEnumerable apiScopes = null) { if (identityResources?.HasDuplicates(m => m.Name) == true) { throw new ArgumentException("Identity resources must not contain duplicate names"); } if (apiResources?.HasDuplicates(m => m.Name) == true) { throw new ArgumentException("Api resources must not contain duplicate names"); } if (apiScopes?.HasDuplicates(m => m.Name) == true) { throw new ArgumentException("Scopes must not contain duplicate names"); } _identityResources = identityResources ?? Enumerable.Empty(); _apiResources = apiResources ?? Enumerable.Empty(); _apiScopes = apiScopes ?? Enumerable.Empty(); } /// public Task GetAllResourcesAsync() { var result = new Resources(_identityResources, _apiResources, _apiScopes); return Task.FromResult(result); } /// public Task> FindApiResourcesByNameAsync(IEnumerable apiResourceNames) { if (apiResourceNames == null) throw new ArgumentNullException(nameof(apiResourceNames)); var query = from a in _apiResources where apiResourceNames.Contains(a.Name) select a; return Task.FromResult(query); } /// public Task> FindIdentityResourcesByScopeNameAsync(IEnumerable scopeNames) { if (scopeNames == null) throw new ArgumentNullException(nameof(scopeNames)); var identity = from i in _identityResources where scopeNames.Contains(i.Name) select i; return Task.FromResult(identity); } /// public Task> FindApiResourcesByScopeNameAsync(IEnumerable scopeNames) { if (scopeNames == null) throw new ArgumentNullException(nameof(scopeNames)); var query = from a in _apiResources where a.Scopes.Any(x => scopeNames.Contains(x)) select a; return Task.FromResult(query); } /// public Task> FindApiScopesByNameAsync(IEnumerable scopeNames) { if (scopeNames == null) throw new ArgumentNullException(nameof(scopeNames)); var query = from x in _apiScopes where scopeNames.Contains(x.Name) select x; return Task.FromResult(query); } } ================================================ FILE: src/IdentityServer8/src/Stores/InMemory/InMemorySigningCredentialsStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Stores; /// /// Default signing credentials store /// /// public class InMemorySigningCredentialsStore : ISigningCredentialStore { private readonly SigningCredentials _credential; /// /// Initializes a new instance of the class. /// /// The credential. public InMemorySigningCredentialsStore(SigningCredentials credential) { _credential = credential; } /// /// Gets the signing credentials. /// /// public Task GetSigningCredentialsAsync() { return Task.FromResult(_credential); } } ================================================ FILE: src/IdentityServer8/src/Stores/InMemory/InMemoryValidationKeysStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// The default validation key store /// /// public class InMemoryValidationKeysStore : IValidationKeysStore { private readonly IEnumerable _keys; /// /// Initializes a new instance of the class. /// /// The keys. /// keys public InMemoryValidationKeysStore(IEnumerable keys) { _keys = keys ?? throw new ArgumentNullException(nameof(keys)); } /// /// Gets all validation keys. /// /// public Task> GetValidationKeysAsync() { return Task.FromResult(_keys); } } ================================================ FILE: src/IdentityServer8/src/Stores/ValidatingClientStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Client store decorator for running runtime configuration validation checks /// public class ValidatingClientStore : IClientStore where T : IClientStore { private readonly IClientStore _inner; private readonly IClientConfigurationValidator _validator; private readonly IEventService _events; private readonly ILogger> _logger; private readonly string _validatorType; /// /// Initializes a new instance of the class. /// /// The inner. /// The validator. /// The events. /// The logger. public ValidatingClientStore(T inner, IClientConfigurationValidator validator, IEventService events, ILogger> logger) { _inner = inner; _validator = validator; _events = events; _logger = logger; _validatorType = validator.GetType().FullName; } /// /// Finds a client by id (and runs the validation logic) /// /// The client id /// /// The client or an InvalidOperationException /// public async Task FindClientByIdAsync(string clientId) { var client = await _inner.FindClientByIdAsync(clientId); if (client != null) { _logger.LogTrace("Calling into client configuration validator: {validatorType}", _validatorType); var context = new ClientConfigurationValidationContext(client); await _validator.ValidateAsync(context); if (context.IsValid) { _logger.LogDebug("client configuration validation for client {clientId} succeeded.", client.ClientId); return client; } _logger.LogError("Invalid client configuration for client {clientId}: {errorMessage}", client.ClientId, context.ErrorMessage); await _events.RaiseAsync(new InvalidClientConfigurationEvent(client, context.ErrorMessage)); return null; } return null; } } ================================================ FILE: src/IdentityServer8/src/Test/IdentityServerBuilderExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; /// /// Extension methods for the IdentityServer builder /// public static class IdentityServerBuilderExtensions { /// /// Adds test users. /// /// The builder. /// The users. /// public static IIdentityServerBuilder AddTestUsers(this IIdentityServerBuilder builder, List users) { builder.Services.AddSingleton(new TestUserStore(users)); builder.AddProfileService(); builder.AddResourceOwnerValidator(); return builder; } } ================================================ FILE: src/IdentityServer8/src/Test/TestUser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Test; /// /// In-memory user object for testing. Not intended for modeling users in production. /// public class TestUser { /// /// Gets or sets the subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the username. /// public string Username { get; set; } /// /// Gets or sets the password. /// public string Password { get; set; } /// /// Gets or sets the provider name. /// public string ProviderName { get; set; } /// /// Gets or sets the provider subject identifier. /// public string ProviderSubjectId { get; set; } /// /// Gets or sets if the user is active. /// public bool IsActive { get; set; } = true; /// /// Gets or sets the claims. /// public ICollection Claims { get; set; } = new HashSet(new ClaimComparer()); } ================================================ FILE: src/IdentityServer8/src/Test/TestUserProfileService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Test; /// /// Profile service for test users /// /// public class TestUserProfileService : IProfileService { /// /// The logger /// protected readonly ILogger Logger; /// /// The users /// protected readonly TestUserStore Users; /// /// Initializes a new instance of the class. /// /// The users. /// The logger. public TestUserProfileService(TestUserStore users, ILogger logger) { Users = users; Logger = logger; } /// /// This method is called whenever claims about the user are requested (e.g. during token creation or via the userinfo endpoint) /// /// The context. /// public virtual Task GetProfileDataAsync(ProfileDataRequestContext context) { context.LogProfileRequest(Logger); if (context.RequestedClaimTypes.Any()) { var user = Users.FindBySubjectId(context.Subject.GetSubjectId()); if (user != null) { context.AddRequestedClaims(user.Claims); } } context.LogIssuedClaims(Logger); return Task.CompletedTask; } /// /// This method gets called whenever identity server needs to determine if the user is valid or active (e.g. if the user's account has been deactivated since they logged in). /// (e.g. during token issuance or validation). /// /// The context. /// public virtual Task IsActiveAsync(IsActiveContext context) { Logger.LogDebug("IsActive called from: {caller}", context.Caller); var user = Users.FindBySubjectId(context.Subject.GetSubjectId()); context.IsActive = user?.IsActive == true; return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Test/TestUserResourceOwnerPasswordValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Test; /// /// Resource owner password validator for test users /// /// public class TestUserResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { private readonly TestUserStore _users; private readonly ISystemClock _clock; /// /// Initializes a new instance of the class. /// /// The users. /// The clock. public TestUserResourceOwnerPasswordValidator(TestUserStore users, ISystemClock clock) { _users = users; _clock = clock; } /// /// Validates the resource owner password credential /// /// The context. /// public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { if (_users.ValidateCredentials(context.UserName, context.Password)) { var user = _users.FindByUsername(context.UserName); context.Result = new GrantValidationResult( user.SubjectId ?? throw new ArgumentException("Subject ID not set", nameof(user.SubjectId)), OidcConstants.AuthenticationMethods.Password, _clock.UtcNow.UtcDateTime, user.Claims); } return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Test/TestUserStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Test; /// /// Store for test users /// public class TestUserStore { private readonly List _users; /// /// Initializes a new instance of the class. /// /// The users. public TestUserStore(List users) { _users = users; } /// /// Validates the credentials. /// /// The username. /// The password. /// public bool ValidateCredentials(string username, string password) { var user = FindByUsername(username); if (user != null) { if (string.IsNullOrWhiteSpace(user.Password) && string.IsNullOrWhiteSpace(password)) { return true; } return user.Password.Equals(password); } return false; } /// /// Finds the user by subject identifier. /// /// The subject identifier. /// public TestUser FindBySubjectId(string subjectId) { return _users.FirstOrDefault(x => x.SubjectId == subjectId); } /// /// Finds the user by username. /// /// The username. /// public TestUser FindByUsername(string username) { return _users.FirstOrDefault(x => x.Username.Equals(username, StringComparison.OrdinalIgnoreCase)); } /// /// Finds the user by external provider. /// /// The provider. /// The user identifier. /// public TestUser FindByExternalProvider(string provider, string userId) { return _users.FirstOrDefault(x => x.ProviderName == provider && x.ProviderSubjectId == userId); } /// /// Automatically provisions a user. /// /// The provider. /// The user identifier. /// The claims. /// public TestUser AutoProvisionUser(string provider, string userId, List claims) { // create a list of claims that we want to transfer into our store var filtered = new List(); foreach (var claim in claims) { // if the external system sends a display name - translate that to the standard OIDC name claim if (claim.Type == ClaimTypes.Name) { filtered.Add(new Claim(JwtClaimTypes.Name, claim.Value)); } // if the JWT handler has an outbound mapping to an OIDC claim use that else if (JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap.ContainsKey(claim.Type)) { filtered.Add(new Claim(JwtSecurityTokenHandler.DefaultOutboundClaimTypeMap[claim.Type], claim.Value)); } // copy the claim as-is else { filtered.Add(claim); } } // if no display name was provided, try to construct by first and/or last name if (!filtered.Any(x => x.Type == JwtClaimTypes.Name)) { var first = filtered.FirstOrDefault(x => x.Type == JwtClaimTypes.GivenName)?.Value; var last = filtered.FirstOrDefault(x => x.Type == JwtClaimTypes.FamilyName)?.Value; if (first != null && last != null) { filtered.Add(new Claim(JwtClaimTypes.Name, first + " " + last)); } else if (first != null) { filtered.Add(new Claim(JwtClaimTypes.Name, first)); } else if (last != null) { filtered.Add(new Claim(JwtClaimTypes.Name, last)); } } // create a new unique subject id var sub = CryptoRandom.CreateUniqueId(format: CryptoRandom.OutputFormat.Hex); // check if a display name is available, otherwise fallback to subject id var name = filtered.FirstOrDefault(c => c.Type == JwtClaimTypes.Name)?.Value ?? sub; // create new user var user = new TestUser { SubjectId = sub, Username = name, ProviderName = provider, ProviderSubjectId = userId, Claims = filtered }; // add user to in-memory store _users.Add(user); return user; } } ================================================ FILE: src/IdentityServer8/src/Validation/Contexts/ClientConfigurationValidationContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Context for client configuration validation. /// public class ClientConfigurationValidationContext { /// /// Gets or sets the client. /// /// /// The client. /// public Client Client { get; } /// /// Returns true if client configuration is valid. /// /// /// true if this instance is valid; otherwise, false. /// public bool IsValid { get; set; } = true; /// /// Gets or sets the error message. /// /// /// The error message. /// public string ErrorMessage { get; set; } /// /// Initializes a new instance of the class. /// /// The client. public ClientConfigurationValidationContext(Client client) { Client = client; } /// /// Sets a validation error. /// /// The message. public void SetError(string message) { IsValid = false; ErrorMessage = message; } } ================================================ FILE: src/IdentityServer8/src/Validation/Contexts/CustomAuthorizeRequestValidationContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Context for custom authorize request validation. /// public class CustomAuthorizeRequestValidationContext { /// /// The result of custom validation. /// public AuthorizeRequestValidationResult Result { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Contexts/CustomTokenRequestValidationContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Context class for custom token request validation /// public class CustomTokenRequestValidationContext { /// /// Gets or sets the result. /// /// /// The result. /// public TokenRequestValidationResult Result { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Contexts/ExtensionGrantValidationContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Class describing the extension grant validation context /// public class ExtensionGrantValidationContext { /// /// Gets or sets the request. /// /// /// The request. /// public ValidatedTokenRequest Request { get; set; } /// /// Gets or sets the result. /// /// /// The result. /// public GrantValidationResult Result { get; set; } = new GrantValidationResult(TokenRequestErrors.InvalidGrant); } ================================================ FILE: src/IdentityServer8/src/Validation/Contexts/ResourceOwnerPasswordValidationContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Class describing the resource owner password validation context /// public class ResourceOwnerPasswordValidationContext { /// /// Gets or sets the name of the user. /// /// /// The name of the user. /// public string UserName { get; set; } /// /// Gets or sets the password. /// /// /// The password. /// public string Password { get; set; } /// /// Gets or sets the request. /// /// /// The request. /// public ValidatedTokenRequest Request { get; set; } /// /// Gets or sets the result. /// /// /// The result. /// public GrantValidationResult Result { get; set; } = new GrantValidationResult(TokenRequestErrors.InvalidGrant); } ================================================ FILE: src/IdentityServer8/src/Validation/Contexts/ResourceValidationContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Class describing the resource validation context /// public class ResourceValidationContext { /// /// Gets or sets the result. /// /// /// The result. /// public GrantValidationResult Result { get; set; } = new GrantValidationResult(TokenRequestErrors.InvalidGrant); } ================================================ FILE: src/IdentityServer8/src/Validation/Default/ApiSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates API secrets using the registered secret validators and parsers /// public class ApiSecretValidator : IApiSecretValidator { private readonly ILogger _logger; private readonly IResourceStore _resources; private readonly IEventService _events; private readonly ISecretsListParser _parser; private readonly ISecretsListValidator _validator; /// /// Initializes a new instance of the class. /// /// The resources. /// The parsers. /// The validator. /// The events. /// The logger. public ApiSecretValidator(IResourceStore resources, ISecretsListParser parsers, ISecretsListValidator validator, IEventService events, ILogger logger) { _resources = resources; _parser = parsers; _validator = validator; _events = events; _logger = logger; } /// /// Validates the secret on the current request. /// /// The context. /// public async Task ValidateAsync(HttpContext context) { _logger.LogTrace("Start API validation"); var fail = new ApiSecretValidationResult { IsError = true }; var parsedSecret = await _parser.ParseAsync(context); if (parsedSecret == null) { await RaiseFailureEventAsync("unknown", "No API id or secret found"); _logger.LogError("No API secret found"); return fail; } // load API resource var apis = await _resources.FindApiResourcesByNameAsync(new[] { parsedSecret.Id }); if (apis == null || !apis.Any()) { await RaiseFailureEventAsync(parsedSecret.Id, "Unknown API resource"); _logger.LogError("No API resource with that name found. aborting"); return fail; } if (apis.Count() > 1) { await RaiseFailureEventAsync(parsedSecret.Id, "Invalid API resource"); _logger.LogError("More than one API resource with that name found. aborting"); return fail; } var api = apis.Single(); if (api.Enabled == false) { await RaiseFailureEventAsync(parsedSecret.Id, "API resource not enabled"); _logger.LogError("API resource not enabled. aborting."); return fail; } var result = await _validator.ValidateAsync(api.ApiSecrets, parsedSecret); if (result.Success) { _logger.LogDebug("API resource validation success"); var success = new ApiSecretValidationResult { IsError = false, Resource = api }; await RaiseSuccessEventAsync(api.Name, parsedSecret.Type); return success; } await RaiseFailureEventAsync(api.Name, "Invalid API secret"); _logger.LogError("API validation failed."); return fail; } private Task RaiseSuccessEventAsync(string clientId, string authMethod) { return _events.RaiseAsync(new ApiAuthenticationSuccessEvent(clientId, authMethod)); } private Task RaiseFailureEventAsync(string clientId, string message) { return _events.RaiseAsync(new ApiAuthenticationFailureEvent(clientId, message)); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/AuthorizeRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; internal class AuthorizeRequestValidator : IAuthorizeRequestValidator { private readonly IdentityServerOptions _options; private readonly IClientStore _clients; private readonly ICustomAuthorizeRequestValidator _customValidator; private readonly IRedirectUriValidator _uriValidator; private readonly IResourceValidator _resourceValidator; private readonly IUserSession _userSession; private readonly JwtRequestValidator _jwtRequestValidator; private readonly IJwtRequestUriHttpClient _jwtRequestUriHttpClient; private readonly ILogger _logger; private readonly ResponseTypeEqualityComparer _responseTypeEqualityComparer = new ResponseTypeEqualityComparer(); public AuthorizeRequestValidator( IdentityServerOptions options, IClientStore clients, ICustomAuthorizeRequestValidator customValidator, IRedirectUriValidator uriValidator, IResourceValidator resourceValidator, IUserSession userSession, JwtRequestValidator jwtRequestValidator, IJwtRequestUriHttpClient jwtRequestUriHttpClient, ILogger logger) { _options = options; _clients = clients; _customValidator = customValidator; _uriValidator = uriValidator; _resourceValidator = resourceValidator; _jwtRequestValidator = jwtRequestValidator; _userSession = userSession; _jwtRequestUriHttpClient = jwtRequestUriHttpClient; _logger = logger; } public async Task ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject = null) { _logger.LogDebug("Start authorize request protocol validation"); var request = new ValidatedAuthorizeRequest { Options = _options, Subject = subject ?? Principal.Anonymous, Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)) }; // load client_id // client_id must always be present on the request var loadClientResult = await LoadClientAsync(request); if (loadClientResult.IsError) { return loadClientResult; } // load request object var roLoadResult = await LoadRequestObjectAsync(request); if (roLoadResult.IsError) { return roLoadResult; } // validate request object var roValidationResult = await ValidateRequestObjectAsync(request); if (roValidationResult.IsError) { return roValidationResult; } // validate client_id and redirect_uri var clientResult = await ValidateClientAsync(request); if (clientResult.IsError) { return clientResult; } // state, response_type, response_mode var mandatoryResult = ValidateCoreParameters(request); if (mandatoryResult.IsError) { return mandatoryResult; } // scope, scope restrictions and plausability var scopeResult = await ValidateScopeAsync(request); if (scopeResult.IsError) { return scopeResult; } // nonce, prompt, acr_values, login_hint etc. var optionalResult = await ValidateOptionalParametersAsync(request); if (optionalResult.IsError) { return optionalResult; } // custom validator _logger.LogDebug("Calling into custom validator: {type}", _customValidator.GetType().FullName); var context = new CustomAuthorizeRequestValidationContext { Result = new AuthorizeRequestValidationResult(request) }; await _customValidator.ValidateAsync(context); var customResult = context.Result; if (customResult.IsError) { LogError("Error in custom validation", customResult.Error, request); return Invalid(request, customResult.Error, customResult.ErrorDescription); } _logger.LogTrace("Authorize request protocol validation successful"); return Valid(request); } private async Task LoadRequestObjectAsync(ValidatedAuthorizeRequest request) { var jwtRequest = request.Raw.Get(OidcConstants.AuthorizeRequest.Request); var jwtRequestUri = request.Raw.Get(OidcConstants.AuthorizeRequest.RequestUri); if (jwtRequest.IsPresent() && jwtRequestUri.IsPresent()) { LogError("Both request and request_uri are present", request); return Invalid(request, description: "Only one request parameter is allowed"); } if (_options.Endpoints.EnableJwtRequestUri) { if (jwtRequestUri.IsPresent()) { // 512 is from the spec if (jwtRequestUri.Length > 512) { LogError("request_uri is too long", request); return Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestUri, description: "request_uri is too long"); } var jwt = await _jwtRequestUriHttpClient.GetJwtAsync(jwtRequestUri, request.Client); if (jwt.IsMissing()) { LogError("no value returned from request_uri", request); return Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestUri, description: "no value returned from request_uri"); } jwtRequest = jwt; } } else if (jwtRequestUri.IsPresent()) { LogError("request_uri present but config prohibits", request); return Invalid(request, error: OidcConstants.AuthorizeErrors.RequestUriNotSupported); } // check length restrictions if (jwtRequest.IsPresent()) { if (jwtRequest.Length >= _options.InputLengthRestrictions.Jwt) { LogError("request value is too long", request); return Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestObject, description: "Invalid request value"); } } request.RequestObject = jwtRequest; return Valid(request); } private async Task LoadClientAsync(ValidatedAuthorizeRequest request) { ////////////////////////////////////////////////////////// // client_id must be present ///////////////////////////////////////////////////////// var clientId = request.Raw.Get(OidcConstants.AuthorizeRequest.ClientId); if (clientId.IsMissingOrTooLong(_options.InputLengthRestrictions.ClientId)) { LogError("client_id is missing or too long", request); return Invalid(request, description: "Invalid client_id"); } request.ClientId = clientId; ////////////////////////////////////////////////////////// // check for valid client ////////////////////////////////////////////////////////// var client = await _clients.FindEnabledClientByIdAsync(request.ClientId); if (client == null) { LogError("Unknown client or not enabled", request.ClientId, request); return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, "Unknown client or client not enabled"); } request.SetClient(client); return Valid(request); } private async Task ValidateRequestObjectAsync(ValidatedAuthorizeRequest request) { ////////////////////////////////////////////////////////// // validate request object ///////////////////////////////////////////////////////// if (request.RequestObject.IsPresent()) { // validate the request JWT for this client var jwtRequestValidationResult = await _jwtRequestValidator.ValidateAsync(request.Client, request.RequestObject); if (jwtRequestValidationResult.IsError) { LogError("request JWT validation failure", request); return Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestObject, description: "Invalid JWT request"); } // validate response_type match var responseType = request.Raw.Get(OidcConstants.AuthorizeRequest.ResponseType); if (responseType != null) { if (jwtRequestValidationResult.Payload.TryGetValue(OidcConstants.AuthorizeRequest.ResponseType, out var payloadResponseType)) { if (payloadResponseType != responseType) { LogError("response_type in JWT payload does not match response_type in request", request); return Invalid(request, description: "Invalid JWT request"); } } } // validate client_id mismatch if (jwtRequestValidationResult.Payload.TryGetValue(OidcConstants.AuthorizeRequest.ClientId, out var payloadClientId)) { if (!string.Equals(request.Client.ClientId, payloadClientId, StringComparison.Ordinal)) { LogError("client_id in JWT payload does not match client_id in request", request); return Invalid(request, description: "Invalid JWT request"); } } else { LogError("client_id is missing in JWT payload", request); return Invalid(request, error: OidcConstants.AuthorizeErrors.InvalidRequestObject, description: "Invalid JWT request"); } var ignoreKeys = new[] { JwtClaimTypes.Issuer, JwtClaimTypes.Audience }; // merge jwt payload values into original request parameters foreach (var key in jwtRequestValidationResult.Payload.Keys) { if (ignoreKeys.Contains(key)) continue; var value = jwtRequestValidationResult.Payload[key]; var qsValue = request.Raw.Get(key); if (qsValue != null) { if (!string.Equals(value, qsValue, StringComparison.Ordinal)) { LogError("parameter mismatch between request object and query string parameter.", request); return Invalid(request, description: "Parameter mismatch in JWT request"); } } request.Raw.Set(key, value); } request.RequestObjectValues = jwtRequestValidationResult.Payload; } return Valid(request); } private async Task ValidateClientAsync(ValidatedAuthorizeRequest request) { ////////////////////////////////////////////////////////// // check request object requirement ////////////////////////////////////////////////////////// if (request.Client.RequireRequestObject) { if (!request.RequestObjectValues.Any()) { return Invalid(request, description: "Client must use request object, but no request or request_uri parameter present"); } } ////////////////////////////////////////////////////////// // redirect_uri must be present, and a valid uri ////////////////////////////////////////////////////////// var redirectUri = request.Raw.Get(OidcConstants.AuthorizeRequest.RedirectUri); if (redirectUri.IsMissingOrTooLong(_options.InputLengthRestrictions.RedirectUri)) { LogError("redirect_uri is missing or too long", request); return Invalid(request, description: "Invalid redirect_uri"); } if (!Uri.TryCreate(redirectUri, UriKind.Absolute, out _)) { LogError("malformed redirect_uri", redirectUri, request); return Invalid(request, description: "Invalid redirect_uri"); } ////////////////////////////////////////////////////////// // check if client protocol type is oidc ////////////////////////////////////////////////////////// if (request.Client.ProtocolType != IdentityServerConstants.ProtocolTypes.OpenIdConnect) { LogError("Invalid protocol type for OIDC authorize endpoint", request.Client.ProtocolType, request); return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, description: "Invalid protocol"); } ////////////////////////////////////////////////////////// // check if redirect_uri is valid ////////////////////////////////////////////////////////// if (await _uriValidator.IsRedirectUriValidAsync(redirectUri, request.Client) == false) { LogError("Invalid redirect_uri", redirectUri, request); return Invalid(request, OidcConstants.AuthorizeErrors.InvalidRequest, "Invalid redirect_uri"); } request.RedirectUri = redirectUri; return Valid(request); } private AuthorizeRequestValidationResult ValidateCoreParameters(ValidatedAuthorizeRequest request) { ////////////////////////////////////////////////////////// // check state ////////////////////////////////////////////////////////// var state = request.Raw.Get(OidcConstants.AuthorizeRequest.State); if (state.IsPresent()) { request.State = state; } ////////////////////////////////////////////////////////// // response_type must be present and supported ////////////////////////////////////////////////////////// var responseType = request.Raw.Get(OidcConstants.AuthorizeRequest.ResponseType); if (responseType.IsMissing()) { LogError("Missing response_type", request); return Invalid(request, OidcConstants.AuthorizeErrors.UnsupportedResponseType, "Missing response_type"); } // The responseType may come in in an unconventional order. // Use an IEqualityComparer that doesn't care about the order of multiple values. // Per https://tools.ietf.org/html/rfc6749#section-3.1.1 - // 'Extension response types MAY contain a space-delimited (%x20) list of // values, where the order of values does not matter (e.g., response // type "a b" is the same as "b a").' // http://openid.net/specs/oauth-v2-multiple-response-types-1_0-03.html#terminology - // 'If a response type contains one of more space characters (%20), it is compared // as a space-delimited list of values in which the order of values does not matter.' if (!Constants.SupportedResponseTypes.Contains(responseType, _responseTypeEqualityComparer)) { LogError("Response type not supported", responseType, request); return Invalid(request, OidcConstants.AuthorizeErrors.UnsupportedResponseType, "Response type not supported"); } // Even though the responseType may have come in in an unconventional order, // we still need the request's ResponseType property to be set to the // conventional, supported response type. request.ResponseType = Constants.SupportedResponseTypes.First( supportedResponseType => _responseTypeEqualityComparer.Equals(supportedResponseType, responseType)); ////////////////////////////////////////////////////////// // match response_type to grant type ////////////////////////////////////////////////////////// request.GrantType = Constants.ResponseTypeToGrantTypeMapping[request.ResponseType]; // set default response mode for flow; this is needed for any client error processing below request.ResponseMode = Constants.AllowedResponseModesForGrantType[request.GrantType].First(); ////////////////////////////////////////////////////////// // check if flow is allowed at authorize endpoint ////////////////////////////////////////////////////////// if (!Constants.AllowedGrantTypesForAuthorizeEndpoint.Contains(request.GrantType)) { LogError("Invalid grant type", request.GrantType, request); return Invalid(request, description: "Invalid response_type"); } ////////////////////////////////////////////////////////// // check if PKCE is required and validate parameters ////////////////////////////////////////////////////////// if (request.GrantType == GrantType.AuthorizationCode || request.GrantType == GrantType.Hybrid) { _logger.LogDebug("Checking for PKCE parameters"); ///////////////////////////////////////////////////////////////////////////// // validate code_challenge and code_challenge_method ///////////////////////////////////////////////////////////////////////////// var proofKeyResult = ValidatePkceParameters(request); if (proofKeyResult.IsError) { return proofKeyResult; } } ////////////////////////////////////////////////////////// // check response_mode parameter and set response_mode ////////////////////////////////////////////////////////// // check if response_mode parameter is present and valid var responseMode = request.Raw.Get(OidcConstants.AuthorizeRequest.ResponseMode); if (responseMode.IsPresent()) { if (Constants.SupportedResponseModes.Contains(responseMode)) { if (Constants.AllowedResponseModesForGrantType[request.GrantType].Contains(responseMode)) { request.ResponseMode = responseMode; } else { LogError("Invalid response_mode for response_type", responseMode, request); return Invalid(request, OidcConstants.AuthorizeErrors.InvalidRequest, description: "Invalid response_mode for response_type"); } } else { LogError("Unsupported response_mode", responseMode, request); return Invalid(request, OidcConstants.AuthorizeErrors.UnsupportedResponseType, description: "Invalid response_mode"); } } ////////////////////////////////////////////////////////// // check if grant type is allowed for client ////////////////////////////////////////////////////////// if (!request.Client.AllowedGrantTypes.Contains(request.GrantType)) { LogError("Invalid grant type for client", request.GrantType, request); return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, "Invalid grant type for client"); } ////////////////////////////////////////////////////////// // check if response type contains an access token, // and if client is allowed to request access token via browser ////////////////////////////////////////////////////////// var responseTypes = responseType.FromSpaceSeparatedString(); if (responseTypes.Contains(OidcConstants.ResponseTypes.Token)) { if (!request.Client.AllowAccessTokensViaBrowser) { LogError("Client requested access token - but client is not configured to receive access tokens via browser", request); return Invalid(request, description: "Client not configured to receive access tokens via browser"); } } return Valid(request); } private AuthorizeRequestValidationResult ValidatePkceParameters(ValidatedAuthorizeRequest request) { var fail = Invalid(request); var codeChallenge = request.Raw.Get(OidcConstants.AuthorizeRequest.CodeChallenge); if (codeChallenge.IsMissing()) { if (request.Client.RequirePkce) { LogError("code_challenge is missing", request); fail.ErrorDescription = "code challenge required"; } else { _logger.LogDebug("No PKCE used."); return Valid(request); } return fail; } if (codeChallenge.Length < _options.InputLengthRestrictions.CodeChallengeMinLength || codeChallenge.Length > _options.InputLengthRestrictions.CodeChallengeMaxLength) { LogError("code_challenge is either too short or too long", request); fail.ErrorDescription = "Invalid code_challenge"; return fail; } request.CodeChallenge = codeChallenge; var codeChallengeMethod = request.Raw.Get(OidcConstants.AuthorizeRequest.CodeChallengeMethod); if (codeChallengeMethod.IsMissing()) { _logger.LogDebug("Missing code_challenge_method, defaulting to plain"); codeChallengeMethod = OidcConstants.CodeChallengeMethods.Plain; } if (!Constants.SupportedCodeChallengeMethods.Contains(codeChallengeMethod)) { LogError("Unsupported code_challenge_method", codeChallengeMethod, request); fail.ErrorDescription = "Transform algorithm not supported"; return fail; } // check if plain method is allowed if (codeChallengeMethod == OidcConstants.CodeChallengeMethods.Plain) { if (!request.Client.AllowPlainTextPkce) { LogError("code_challenge_method of plain is not allowed", request); fail.ErrorDescription = "Transform algorithm not supported"; return fail; } } request.CodeChallengeMethod = codeChallengeMethod; return Valid(request); } private async Task ValidateScopeAsync(ValidatedAuthorizeRequest request) { ////////////////////////////////////////////////////////// // scope must be present ////////////////////////////////////////////////////////// var scope = request.Raw.Get(OidcConstants.AuthorizeRequest.Scope); if (scope.IsMissing()) { LogError("scope is missing", request); return Invalid(request, description: "Invalid scope"); } if (scope.Length > _options.InputLengthRestrictions.Scope) { LogError("scopes too long.", request); return Invalid(request, description: "Invalid scope"); } request.RequestedScopes = scope.FromSpaceSeparatedString().Distinct().ToList(); if (request.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OpenId)) { request.IsOpenIdRequest = true; } ////////////////////////////////////////////////////////// // check scope vs response_type plausability ////////////////////////////////////////////////////////// var requirement = Constants.ResponseTypeToScopeRequirement[request.ResponseType]; if (requirement == Constants.ScopeRequirement.Identity || requirement == Constants.ScopeRequirement.IdentityOnly) { if (request.IsOpenIdRequest == false) { LogError("response_type requires the openid scope", request); return Invalid(request, description: "Missing openid scope"); } } ////////////////////////////////////////////////////////// // check if scopes are valid/supported and check for resource scopes ////////////////////////////////////////////////////////// var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest { Client = request.Client, Scopes = request.RequestedScopes }); if (!validatedResources.Succeeded) { return Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope, "Invalid scope"); } if (validatedResources.Resources.IdentityResources.Any() && !request.IsOpenIdRequest) { LogError("Identity related scope requests, but no openid scope", request); return Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope, "Identity scopes requested, but openid scope is missing"); } if (validatedResources.Resources.ApiScopes.Any()) { request.IsApiResourceRequest = true; } ////////////////////////////////////////////////////////// // check id vs resource scopes and response types plausability ////////////////////////////////////////////////////////// var responseTypeValidationCheck = true; switch (requirement) { case Constants.ScopeRequirement.Identity: if (!validatedResources.Resources.IdentityResources.Any()) { _logger.LogError("Requests for id_token response type must include identity scopes"); responseTypeValidationCheck = false; } break; case Constants.ScopeRequirement.IdentityOnly: if (!validatedResources.Resources.IdentityResources.Any() || validatedResources.Resources.ApiScopes.Any()) { _logger.LogError("Requests for id_token response type only must not include resource scopes"); responseTypeValidationCheck = false; } break; case Constants.ScopeRequirement.ResourceOnly: if (validatedResources.Resources.IdentityResources.Any() || !validatedResources.Resources.ApiScopes.Any()) { _logger.LogError("Requests for token response type only must include resource scopes, but no identity scopes."); responseTypeValidationCheck = false; } break; } if (!responseTypeValidationCheck) { return Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope, "Invalid scope for response type"); } request.ValidatedResources = validatedResources; return Valid(request); } private async Task ValidateOptionalParametersAsync(ValidatedAuthorizeRequest request) { ////////////////////////////////////////////////////////// // check nonce ////////////////////////////////////////////////////////// var nonce = request.Raw.Get(OidcConstants.AuthorizeRequest.Nonce); if (nonce.IsPresent()) { if (nonce.Length > _options.InputLengthRestrictions.Nonce) { LogError("Nonce too long", request); return Invalid(request, description: "Invalid nonce"); } request.Nonce = nonce; } else { if (request.GrantType == GrantType.Implicit || request.GrantType == GrantType.Hybrid) { // only openid requests require nonce if (request.IsOpenIdRequest) { LogError("Nonce required for implicit and hybrid flow with openid scope", request); return Invalid(request, description: "Invalid nonce"); } } } ////////////////////////////////////////////////////////// // check prompt ////////////////////////////////////////////////////////// var prompt = request.Raw.Get(OidcConstants.AuthorizeRequest.Prompt); if (prompt.IsPresent()) { var prompts = prompt.Split(' ', StringSplitOptions.RemoveEmptyEntries); if (prompts.All(p => Constants.SupportedPromptModes.Contains(p))) { if (prompts.Contains(OidcConstants.PromptModes.None) && prompts.Length > 1) { LogError("prompt contains 'none' and other values. 'none' should be used by itself.", request); return Invalid(request, description: "Invalid prompt"); } request.PromptModes = prompts; } else { _logger.LogDebug("Unsupported prompt mode - ignored: " + prompt); } } ////////////////////////////////////////////////////////// // check ui locales ////////////////////////////////////////////////////////// var uilocales = request.Raw.Get(OidcConstants.AuthorizeRequest.UiLocales); if (uilocales.IsPresent()) { if (uilocales.Length > _options.InputLengthRestrictions.UiLocale) { LogError("UI locale too long", request); return Invalid(request, description: "Invalid ui_locales"); } request.UiLocales = uilocales; } ////////////////////////////////////////////////////////// // check display ////////////////////////////////////////////////////////// var display = request.Raw.Get(OidcConstants.AuthorizeRequest.Display); if (display.IsPresent()) { if (Constants.SupportedDisplayModes.Contains(display)) { request.DisplayMode = display; } _logger.LogDebug("Unsupported display mode - ignored: " + display); } ////////////////////////////////////////////////////////// // check max_age ////////////////////////////////////////////////////////// var maxAge = request.Raw.Get(OidcConstants.AuthorizeRequest.MaxAge); if (maxAge.IsPresent()) { if (int.TryParse(maxAge, out var seconds)) { if (seconds >= 0) { request.MaxAge = seconds; } else { LogError("Invalid max_age.", request); return Invalid(request, description: "Invalid max_age"); } } else { LogError("Invalid max_age.", request); return Invalid(request, description: "Invalid max_age"); } } ////////////////////////////////////////////////////////// // check login_hint ////////////////////////////////////////////////////////// var loginHint = request.Raw.Get(OidcConstants.AuthorizeRequest.LoginHint); if (loginHint.IsPresent()) { if (loginHint.Length > _options.InputLengthRestrictions.LoginHint) { LogError("Login hint too long", request); return Invalid(request, description: "Invalid login_hint"); } request.LoginHint = loginHint; } ////////////////////////////////////////////////////////// // check acr_values ////////////////////////////////////////////////////////// var acrValues = request.Raw.Get(OidcConstants.AuthorizeRequest.AcrValues); if (acrValues.IsPresent()) { if (acrValues.Length > _options.InputLengthRestrictions.AcrValues) { LogError("Acr values too long", request); return Invalid(request, description: "Invalid acr_values"); } request.AuthenticationContextReferenceClasses = acrValues.FromSpaceSeparatedString().Distinct().ToList(); } ////////////////////////////////////////////////////////// // check custom acr_values: idp ////////////////////////////////////////////////////////// var idp = request.GetIdP(); if (idp.IsPresent()) { // if idp is present but client does not allow it, strip it from the request message if (request.Client.IdentityProviderRestrictions != null && request.Client.IdentityProviderRestrictions.Any()) { if (!request.Client.IdentityProviderRestrictions.Contains(idp)) { _logger.LogWarning("idp requested ({idp}) is not in client restriction list.", idp); request.RemoveIdP(); } } } ////////////////////////////////////////////////////////// // check session cookie ////////////////////////////////////////////////////////// if (_options.Endpoints.EnableCheckSessionEndpoint) { if (request.Subject.IsAuthenticated()) { var sessionId = await _userSession.GetSessionIdAsync(); if (sessionId.IsPresent()) { request.SessionId = sessionId; } else { LogError("Check session endpoint enabled, but SessionId is missing", request); } } else { request.SessionId = ""; // empty string for anonymous users } } return Valid(request); } private AuthorizeRequestValidationResult Invalid(ValidatedAuthorizeRequest request, string error = OidcConstants.AuthorizeErrors.InvalidRequest, string description = null) { return new AuthorizeRequestValidationResult(request, error, description); } private AuthorizeRequestValidationResult Valid(ValidatedAuthorizeRequest request) { return new AuthorizeRequestValidationResult(request); } private void LogError(string message, ValidatedAuthorizeRequest request) { var requestDetails = new AuthorizeRequestValidationLog(request, _options.Logging.AuthorizeRequestSensitiveValuesFilter); _logger.LogError(message + "\n{@requestDetails}", requestDetails); } private void LogError(string message, string detail, ValidatedAuthorizeRequest request) { var requestDetails = new AuthorizeRequestValidationLog(request, _options.Logging.AuthorizeRequestSensitiveValuesFilter); _logger.LogError(message + ": {detail}\n{@requestDetails}", detail, requestDetails); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/BasicAuthenticationSecretParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Parses a Basic Authentication header /// public class BasicAuthenticationSecretParser : ISecretParser { private readonly ILogger _logger; private readonly IdentityServerOptions _options; /// /// Creates the parser with a reference to identity server options /// /// IdentityServer options /// The logger public BasicAuthenticationSecretParser(IdentityServerOptions options, ILogger logger) { _options = options; _logger = logger; } /// /// Returns the authentication method name that this parser implements /// /// /// The authentication method. /// public string AuthenticationMethod => OidcConstants.EndpointAuthenticationMethods.BasicAuthentication; /// /// Tries to find a secret that can be used for authentication /// /// /// A parsed secret /// public Task ParseAsync(HttpContext context) { _logger.LogDebug("Start parsing Basic Authentication secret"); var notfound = Task.FromResult(null); var authorizationHeader = context.Request.Headers["Authorization"].FirstOrDefault(); if (authorizationHeader.IsMissing()) { return notfound; } if (!authorizationHeader.StartsWith("Basic ", StringComparison.OrdinalIgnoreCase)) { return notfound; } var parameter = authorizationHeader.Substring("Basic ".Length); string pair; try { pair = Encoding.UTF8.GetString( Convert.FromBase64String(parameter)); } catch (FormatException) { _logger.LogWarning("Malformed Basic Authentication credential."); return notfound; } catch (ArgumentException) { _logger.LogWarning("Malformed Basic Authentication credential."); return notfound; } var ix = pair.IndexOf(':'); if (ix == -1) { _logger.LogWarning("Malformed Basic Authentication credential."); return notfound; } var clientId = pair.Substring(0, ix); var secret = pair.Substring(ix + 1); if (clientId.IsPresent()) { if (clientId.Length > _options.InputLengthRestrictions.ClientId) { _logger.LogError("Client ID exceeds maximum length."); return notfound; } if (secret.IsPresent()) { if (secret.Length > _options.InputLengthRestrictions.ClientSecret) { _logger.LogError("Client secret exceeds maximum length."); return notfound; } var parsedSecret = new ParsedSecret { Id = Decode(clientId), Credential = Decode(secret), Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; return Task.FromResult(parsedSecret); } else { // client secret is optional _logger.LogDebug("client id without secret found"); var parsedSecret = new ParsedSecret { Id = Decode(clientId), Type = IdentityServerConstants.ParsedSecretTypes.NoSecret }; return Task.FromResult(parsedSecret); } } _logger.LogDebug("No Basic Authentication secret found"); return notfound; } // RFC6749 says individual values must be application/x-www-form-urlencoded // 2.3.1 private string Decode(string value) { if (value.IsMissing()) return string.Empty; return Uri.UnescapeDataString(value.Replace("+", "%20")); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/BearerTokenUsageValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates a request that uses a bearer token for authentication /// internal class BearerTokenUsageValidator { private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The logger. public BearerTokenUsageValidator(ILogger logger) { _logger = logger; } /// /// Validates the request. /// /// The context. /// public async Task ValidateAsync(HttpContext context) { var result = ValidateAuthorizationHeader(context); if (result.TokenFound) { _logger.LogDebug("Bearer token found in header"); return result; } if (context.Request.HasApplicationFormContentType()) { result = await ValidatePostBodyAsync(context); if (result.TokenFound) { _logger.LogDebug("Bearer token found in body"); return result; } } _logger.LogDebug("Bearer token not found"); return new BearerTokenUsageValidationResult(); } /// /// Validates the authorization header. /// /// The context. /// public BearerTokenUsageValidationResult ValidateAuthorizationHeader(HttpContext context) { var authorizationHeader = context.Request.Headers["Authorization"].FirstOrDefault(); if (authorizationHeader.IsPresent()) { var header = authorizationHeader.Trim(); if (header.StartsWith(OidcConstants.AuthenticationSchemes.AuthorizationHeaderBearer)) { var value = header.Substring(OidcConstants.AuthenticationSchemes.AuthorizationHeaderBearer.Length).Trim(); if (value.IsPresent()) { return new BearerTokenUsageValidationResult { TokenFound = true, Token = value, UsageType = BearerTokenUsageType.AuthorizationHeader }; } } else { _logger.LogTrace("Unexpected header format: {header}", Ioc.Sanitizer.Log.Sanitize(header)); } } return new BearerTokenUsageValidationResult(); } /// /// Validates the post body. /// /// The context. /// public async Task ValidatePostBodyAsync(HttpContext context) { var token = (await context.Request.ReadFormAsync())["access_token"].FirstOrDefault(); if (token.IsPresent()) { return new BearerTokenUsageValidationResult { TokenFound = true, Token = token, UsageType = BearerTokenUsageType.PostBody }; } return new BearerTokenUsageValidationResult(); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/ClientSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates a client secret using the registered secret validators and parsers /// public class ClientSecretValidator : IClientSecretValidator { private readonly ILogger _logger; private readonly IClientStore _clients; private readonly IEventService _events; private readonly ISecretsListValidator _validator; private readonly ISecretsListParser _parser; /// /// Initializes a new instance of the class. /// /// The clients. /// The parser. /// The validator. /// The events. /// The logger. public ClientSecretValidator(IClientStore clients, ISecretsListParser parser, ISecretsListValidator validator, IEventService events, ILogger logger) { _clients = clients; _parser = parser; _validator = validator; _events = events; _logger = logger; } /// /// Validates the current request. /// /// The context. /// public async Task ValidateAsync(HttpContext context) { _logger.LogDebug("Start client validation"); var fail = new ClientSecretValidationResult { IsError = true }; var parsedSecret = await _parser.ParseAsync(context); if (parsedSecret == null) { await RaiseFailureEventAsync("unknown", "No client id found"); _logger.LogError("No client identifier found"); return fail; } // load client var client = await _clients.FindEnabledClientByIdAsync(parsedSecret.Id); if (client == null) { await RaiseFailureEventAsync(parsedSecret.Id, "Unknown client"); _logger.LogError("No client with id '{clientId}' found. aborting", Ioc.Sanitizer.Log.Sanitize(parsedSecret.Id)); return fail; } SecretValidationResult secretValidationResult = null; if (!client.RequireClientSecret || client.IsImplicitOnly()) { _logger.LogDebug("Public Client - skipping secret validation success"); } else { secretValidationResult = await _validator.ValidateAsync(client.ClientSecrets, parsedSecret); if (secretValidationResult.Success == false) { await RaiseFailureEventAsync(client.ClientId, "Invalid client secret"); _logger.LogError("Client secret validation failed for client: {clientId}.", client.ClientId); return fail; } } _logger.LogDebug("Client validation success"); var success = new ClientSecretValidationResult { IsError = false, Client = client, Secret = parsedSecret, Confirmation = secretValidationResult?.Confirmation }; await RaiseSuccessEventAsync(client.ClientId, parsedSecret.Type); return success; } private Task RaiseSuccessEventAsync(string clientId, string authMethod) { return _events.RaiseAsync(new ClientAuthenticationSuccessEvent(clientId, authMethod)); } private Task RaiseFailureEventAsync(string clientId, string message) { return _events.RaiseAsync(new ClientAuthenticationFailureEvent(clientId, message)); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/DefaultClientConfigurationValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default client configuration validator /// /// public class DefaultClientConfigurationValidator : IClientConfigurationValidator { private readonly IdentityServerOptions _options; /// /// Constructor for DefaultClientConfigurationValidator /// public DefaultClientConfigurationValidator(IdentityServerOptions options) { _options = options; } /// /// Determines whether the configuration of a client is valid. /// /// The context. /// public async Task ValidateAsync(ClientConfigurationValidationContext context) { if (context.Client.ProtocolType == IdentityServerConstants.ProtocolTypes.OpenIdConnect) { await ValidateGrantTypesAsync(context); if (context.IsValid == false) return; await ValidateLifetimesAsync(context); if (context.IsValid == false) return; await ValidateRedirectUriAsync(context); if (context.IsValid == false) return; await ValidateAllowedCorsOriginsAsync(context); if (context.IsValid == false) return; await ValidateUriSchemesAsync(context); if (context.IsValid == false) return; await ValidateSecretsAsync(context); if (context.IsValid == false) return; await ValidatePropertiesAsync(context); if (context.IsValid == false) return; } } /// /// Validates grant type related configuration settings. /// /// The context. /// protected virtual Task ValidateGrantTypesAsync(ClientConfigurationValidationContext context) { if (context.Client.AllowedGrantTypes?.Any() != true) { context.SetError("no allowed grant type specified"); } return Task.CompletedTask; } /// /// Validates lifetime related configuration settings. /// /// The context. /// protected virtual Task ValidateLifetimesAsync(ClientConfigurationValidationContext context) { if (context.Client.AccessTokenLifetime <= 0) { context.SetError("access token lifetime is 0 or negative"); return Task.CompletedTask; } if (context.Client.IdentityTokenLifetime <= 0) { context.SetError("identity token lifetime is 0 or negative"); return Task.CompletedTask; } if (context.Client.AllowedGrantTypes?.Contains(GrantType.DeviceFlow) == true && context.Client.DeviceCodeLifetime <= 0) { context.SetError("device code lifetime is 0 or negative"); } // 0 means unlimited lifetime if (context.Client.AbsoluteRefreshTokenLifetime < 0) { context.SetError("absolute refresh token lifetime is negative"); return Task.CompletedTask; } // 0 might mean that sliding is disabled if (context.Client.SlidingRefreshTokenLifetime < 0) { context.SetError("sliding refresh token lifetime is negative"); return Task.CompletedTask; } return Task.CompletedTask; } /// /// Validates redirect URI related configuration. /// /// The context. /// protected virtual Task ValidateRedirectUriAsync(ClientConfigurationValidationContext context) { if (context.Client.AllowedGrantTypes?.Any() == true) { if (context.Client.AllowedGrantTypes.Contains(GrantType.AuthorizationCode) || context.Client.AllowedGrantTypes.Contains(GrantType.Hybrid) || context.Client.AllowedGrantTypes.Contains(GrantType.Implicit)) { if (context.Client.RedirectUris?.Any() == false) { context.SetError("No redirect URI configured."); } } } return Task.CompletedTask; } /// /// Validates allowed CORS origins for valid format. /// /// The context. /// protected virtual Task ValidateAllowedCorsOriginsAsync(ClientConfigurationValidationContext context) { if (context.Client.AllowedCorsOrigins?.Any() == true) { foreach (var origin in context.Client.AllowedCorsOrigins) { var fail = true; if (!string.IsNullOrWhiteSpace(origin) && Uri.TryCreate(origin, UriKind.Absolute, out var uri)) { if (uri.AbsolutePath == "/" && !origin.EndsWith("/")) { fail = false; } } if (fail) { if (!string.IsNullOrWhiteSpace(origin)) { context.SetError($"AllowedCorsOrigins contains invalid origin: {origin}"); } else { context.SetError($"AllowedCorsOrigins contains invalid origin. There is an empty value."); } return Task.CompletedTask; } } } return Task.CompletedTask; } /// /// Validates that URI schemes is not in the list of invalid URI scheme prefixes, as controlled by the ValidationOptions. /// /// /// protected virtual Task ValidateUriSchemesAsync(ClientConfigurationValidationContext context) { if (context.Client.RedirectUris?.Any() == true) { foreach (var uri in context.Client.RedirectUris) { if (_options.Validation.InvalidRedirectUriPrefixes .Any(scheme => uri?.StartsWith(scheme, StringComparison.OrdinalIgnoreCase) == true)) { context.SetError($"RedirectUri '{uri}' uses invalid scheme. If this scheme should be allowed, then configure it via ValidationOptions."); } } } if (context.Client.PostLogoutRedirectUris?.Any() == true) { foreach (var uri in context.Client.PostLogoutRedirectUris) { if (_options.Validation.InvalidRedirectUriPrefixes .Any(scheme => uri?.StartsWith(scheme, StringComparison.OrdinalIgnoreCase) == true)) { context.SetError($"PostLogoutRedirectUri '{uri}' uses invalid scheme. If this scheme should be allowed, then configure it via ValidationOptions."); } } } return Task.CompletedTask; } /// /// Validates secret related configuration. /// /// The context. /// protected virtual Task ValidateSecretsAsync(ClientConfigurationValidationContext context) { if (context.Client.AllowedGrantTypes?.Any() == true) { foreach (var grantType in context.Client.AllowedGrantTypes) { if (!string.Equals(grantType, GrantType.Implicit)) { if (context.Client.RequireClientSecret && context.Client.ClientSecrets.Count == 0) { context.SetError($"Client secret is required for {grantType}, but no client secret is configured."); return Task.CompletedTask; } } } } return Task.CompletedTask; } /// /// Validates properties related configuration settings. /// /// The context. /// protected virtual Task ValidatePropertiesAsync(ClientConfigurationValidationContext context) { return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/DefaultCustomAuthorizeRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default custom request validator /// internal class DefaultCustomAuthorizeRequestValidator : ICustomAuthorizeRequestValidator { /// /// Custom validation logic for the authorize request. /// /// The context. public Task ValidateAsync(CustomAuthorizeRequestValidationContext context) { return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/DefaultCustomTokenRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default custom request validator /// internal class DefaultCustomTokenRequestValidator : ICustomTokenRequestValidator { /// /// Custom validation logic for a token request. /// /// The context. /// /// The validation result /// public Task ValidateAsync(CustomTokenRequestValidationContext context) { return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/DefaultCustomTokenValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default custom token validator /// public class DefaultCustomTokenValidator : ICustomTokenValidator { /// /// The logger /// protected readonly ILogger Logger; /// /// The user service /// protected readonly IProfileService Profile; /// /// The client store /// protected readonly IClientStore Clients; /// /// Custom validation logic for access tokens. /// /// The validation result so far. /// /// The validation result /// public virtual Task ValidateAccessTokenAsync(TokenValidationResult result) { return Task.FromResult(result); } /// /// Custom validation logic for identity tokens. /// /// The validation result so far. /// /// The validation result /// public virtual Task ValidateIdentityTokenAsync(TokenValidationResult result) { return Task.FromResult(result); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/DefaultResourceValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default implementation of IResourceValidator. /// public class DefaultResourceValidator : IResourceValidator { private readonly ILogger _logger; private readonly IScopeParser _scopeParser; private readonly IResourceStore _store; /// /// Initializes a new instance of the class. /// /// The store. /// /// The logger. public DefaultResourceValidator(IResourceStore store, IScopeParser scopeParser, ILogger logger) { _logger = logger; _scopeParser = scopeParser; _store = store; } /// public virtual async Task ValidateRequestedResourcesAsync(ResourceValidationRequest request) { if (request == null) throw new ArgumentNullException(nameof(request)); var parsedScopesResult = _scopeParser.ParseScopeValues(request.Scopes); var result = new ResourceValidationResult(); if (!parsedScopesResult.Succeeded) { foreach (var invalidScope in parsedScopesResult.Errors) { _logger.LogError("Invalid parsed scope {scope}, message: {error}", Ioc.Sanitizer.Log.Sanitize(invalidScope.RawValue), Ioc.Sanitizer.Log.Sanitize(invalidScope.Error)); result.InvalidScopes.Add(invalidScope.RawValue); } return result; } var scopeNames = parsedScopesResult.ParsedScopes.Select(x => x.ParsedName).Distinct().ToArray(); var resourcesFromStore = await _store.FindEnabledResourcesByScopeAsync(scopeNames); foreach (var scope in parsedScopesResult.ParsedScopes) { await ValidateScopeAsync(request.Client, resourcesFromStore, scope, result); } if (result.InvalidScopes.Count > 0) { result.Resources.IdentityResources.Clear(); result.Resources.ApiResources.Clear(); result.Resources.ApiScopes.Clear(); result.ParsedScopes.Clear(); } return result; } /// /// Validates that the requested scopes is contained in the store, and the client is allowed to request it. /// /// /// /// /// /// protected virtual async Task ValidateScopeAsync( Client client, Resources resourcesFromStore, ParsedScopeValue requestedScope, ResourceValidationResult result) { if (requestedScope.ParsedName == IdentityServerConstants.StandardScopes.OfflineAccess) { if (await IsClientAllowedOfflineAccessAsync(client)) { result.Resources.OfflineAccess = true; result.ParsedScopes.Add(new ParsedScopeValue(IdentityServerConstants.StandardScopes.OfflineAccess)); } else { result.InvalidScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } } else { var identity = resourcesFromStore.FindIdentityResourcesByScope(requestedScope.ParsedName); if (identity != null) { if (await IsClientAllowedIdentityResourceAsync(client, identity)) { result.ParsedScopes.Add(requestedScope); result.Resources.IdentityResources.Add(identity); } else { result.InvalidScopes.Add(requestedScope.RawValue); } } else { var apiScope = resourcesFromStore.FindApiScope(requestedScope.ParsedName); if (apiScope != null) { if (await IsClientAllowedApiScopeAsync(client, apiScope)) { result.ParsedScopes.Add(requestedScope); result.Resources.ApiScopes.Add(apiScope); var apis = resourcesFromStore.FindApiResourcesByScope(apiScope.Name); foreach (var api in apis) { result.Resources.ApiResources.Add(api); } } else { result.InvalidScopes.Add(requestedScope.RawValue); } } else { _logger.LogError("Scope {scope} not found in store.", requestedScope.ParsedName); result.InvalidScopes.Add(requestedScope.RawValue); } } } } /// /// Determines if client is allowed access to the identity scope. /// /// /// /// protected virtual Task IsClientAllowedIdentityResourceAsync(Client client, IdentityResource identity) { var allowed = client.AllowedScopes.Contains(identity.Name); if (!allowed) { _logger.LogError("Client {client} is not allowed access to scope {scope}.", client.ClientId, identity.Name); } return Task.FromResult(allowed); } /// /// Determines if client is allowed access to the API scope. /// /// /// /// protected virtual Task IsClientAllowedApiScopeAsync(Client client, ApiScope apiScope) { var allowed = client.AllowedScopes.Contains(apiScope.Name); if (!allowed) { _logger.LogError("Client {client} is not allowed access to scope {scope}.", client.ClientId, apiScope.Name); } return Task.FromResult(allowed); } /// /// Validates if the client is allowed offline_access. /// /// /// protected virtual Task IsClientAllowedOfflineAccessAsync(Client client) { var allowed = client.AllowOfflineAccess; if (!allowed) { _logger.LogError("Client {client} is not allowed access to scope offline_access (via AllowOfflineAccess setting).", client.ClientId); } return Task.FromResult(allowed); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/DefaultScopeParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default implementation of IScopeParser. /// public class DefaultScopeParser : IScopeParser { private readonly ILogger _logger; /// /// Ctor. /// /// public DefaultScopeParser(ILogger logger) { _logger = logger; } /// public ParsedScopesResult ParseScopeValues(IEnumerable scopeValues) { if (scopeValues == null) throw new ArgumentNullException(nameof(scopeValues)); var result = new ParsedScopesResult(); foreach (var scopeValue in scopeValues) { var ctx = new ParseScopeContext(scopeValue); ParseScopeValue(ctx); if (ctx.Succeeded) { var parsedScope = ctx.ParsedName != null ? new ParsedScopeValue(ctx.RawValue, ctx.ParsedName, ctx.ParsedParameter) : new ParsedScopeValue(ctx.RawValue); result.ParsedScopes.Add(parsedScope); } else if (!ctx.Ignore) { result.Errors.Add(new ParsedScopeValidationError(scopeValue, ctx.Error)); } else { _logger.LogDebug("Scope parsing ignoring scope {scope}", Ioc.Sanitizer.Log.Sanitize(scopeValue)); } } return result; } /// /// Parses a scope value. /// /// /// public virtual void ParseScopeValue(ParseScopeContext scopeContext) { // nop leaves the raw scope value as a success result. } /// /// Models the context for parsing a scope. /// public class ParseScopeContext { /// /// The original (raw) value of the scope. /// public string RawValue { get; } /// /// The parsed name of the scope. /// public string ParsedName { get; private set; } /// /// The parsed parameter value of the scope. /// public string ParsedParameter { get; private set; } /// /// The error encountered parsing the scope. /// public string Error { get; private set; } /// /// Indicates if the scope should be excluded from the parsed results. /// public bool Ignore { get; private set; } /// /// Indicates if parsing the scope was successful. /// public bool Succeeded => !Ignore && Error == null; /// /// Ctor. Indicates success, but the scope should not be included in result. /// internal ParseScopeContext(string rawScopeValue) { RawValue = rawScopeValue; } /// /// Sets the parsed name and parsed parameter value for the scope. /// /// /// public void SetParsedValues(string parsedName, string parsedParameter) { if (String.IsNullOrWhiteSpace(parsedName)) { throw new ArgumentNullException(nameof(parsedName)); } if (String.IsNullOrWhiteSpace(parsedParameter)) { throw new ArgumentNullException(nameof(parsedParameter)); } ParsedName = parsedName; ParsedParameter = parsedParameter; Error = null; Ignore = false; } /// /// Set the error encountered parsing the scope. /// /// public void SetError(string error) { ParsedName = null; ParsedParameter = null; Error = error; Ignore = false; } /// /// Sets that the scope is to be ignore/excluded from the parsed results. /// public void SetIgnore() { ParsedName = null; ParsedParameter = null; Error = null; Ignore = true; } } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/DeviceAuthorizationRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; internal class DeviceAuthorizationRequestValidator : IDeviceAuthorizationRequestValidator { private readonly IdentityServerOptions _options; private readonly IResourceValidator _resourceValidator; private readonly ILogger _logger; public DeviceAuthorizationRequestValidator( IdentityServerOptions options, IResourceValidator resourceValidator, ILogger logger) { _options = options; _resourceValidator = resourceValidator; _logger = logger; } public async Task ValidateAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult) { _logger.LogDebug("Start device authorization request validation"); var request = new ValidatedDeviceAuthorizationRequest { Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)), Options = _options }; var clientResult = ValidateClient(request, clientValidationResult); if (clientResult.IsError) { return clientResult; } var scopeResult = await ValidateScopeAsync(request); if (scopeResult.IsError) { return scopeResult; } _logger.LogDebug("{clientId} device authorization request validation success", request.Client.ClientId); return Valid(request); } private DeviceAuthorizationRequestValidationResult Valid(ValidatedDeviceAuthorizationRequest request) { return new DeviceAuthorizationRequestValidationResult(request); } private DeviceAuthorizationRequestValidationResult Invalid(ValidatedDeviceAuthorizationRequest request, string error = OidcConstants.AuthorizeErrors.InvalidRequest, string description = null) { return new DeviceAuthorizationRequestValidationResult(request, error, description); } private void LogError(string message, ValidatedDeviceAuthorizationRequest request) { var requestDetails = new DeviceAuthorizationRequestValidationLog(request); _logger.LogError(message + "\n{requestDetails}", requestDetails); } private void LogError(string message, string detail, ValidatedDeviceAuthorizationRequest request) { var requestDetails = new DeviceAuthorizationRequestValidationLog(request); _logger.LogError(message + ": {detail}\n{requestDetails}", detail, requestDetails); } private DeviceAuthorizationRequestValidationResult ValidateClient(ValidatedDeviceAuthorizationRequest request, ClientSecretValidationResult clientValidationResult) { ////////////////////////////////////////////////////////// // set client & secret ////////////////////////////////////////////////////////// if (clientValidationResult == null) throw new ArgumentNullException(nameof(clientValidationResult)); request.SetClient(clientValidationResult.Client, clientValidationResult.Secret); ////////////////////////////////////////////////////////// // check if client protocol type is oidc ////////////////////////////////////////////////////////// if (request.Client.ProtocolType != IdentityServerConstants.ProtocolTypes.OpenIdConnect) { LogError("Invalid protocol type for OIDC authorize endpoint", request.Client.ProtocolType, request); return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, "Invalid protocol"); } ////////////////////////////////////////////////////////// // check if client allows device flow ////////////////////////////////////////////////////////// if (!request.Client.AllowedGrantTypes.Contains(GrantType.DeviceFlow)) { LogError("Client not configured for device flow", GrantType.DeviceFlow, request); return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient); } return Valid(request); } private async Task ValidateScopeAsync(ValidatedDeviceAuthorizationRequest request) { ////////////////////////////////////////////////////////// // scope must be present ////////////////////////////////////////////////////////// var scope = request.Raw.Get(OidcConstants.AuthorizeRequest.Scope); if (scope.IsMissing()) { _logger.LogTrace("Client provided no scopes - checking allowed scopes list"); if (!request.Client.AllowedScopes.EnumerableIsNullOrEmpty()) { var clientAllowedScopes = new List(request.Client.AllowedScopes); if (request.Client.AllowOfflineAccess) { clientAllowedScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } scope = clientAllowedScopes.ToSpaceSeparatedString(); _logger.LogTrace("Defaulting to: {scopes}", scope); } else { LogError("No allowed scopes configured for client", request); return Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope); } } if (scope.Length > _options.InputLengthRestrictions.Scope) { LogError("scopes too long.", request); return Invalid(request, description: "Invalid scope"); } request.RequestedScopes = scope.FromSpaceSeparatedString().Distinct().ToList(); if (request.RequestedScopes.Contains(IdentityServerConstants.StandardScopes.OpenId)) { request.IsOpenIdRequest = true; } ////////////////////////////////////////////////////////// // check if scopes are valid/supported ////////////////////////////////////////////////////////// var validatedResources = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest{ Client = request.Client, Scopes = request.RequestedScopes }); if (!validatedResources.Succeeded) { if (validatedResources.InvalidScopes.Count > 0) { return Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope); } return Invalid(request, OidcConstants.AuthorizeErrors.UnauthorizedClient, "Invalid scope"); } if (validatedResources.Resources.IdentityResources.Any() && !request.IsOpenIdRequest) { LogError("Identity related scope requests, but no openid scope", request); return Invalid(request, OidcConstants.AuthorizeErrors.InvalidScope); } request.ValidatedResources = validatedResources; return Valid(request); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/DeviceCodeValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates an incoming token request using the device flow /// internal class DeviceCodeValidator : IDeviceCodeValidator { private readonly IDeviceFlowCodeService _devices; private readonly IProfileService _profile; private readonly IDeviceFlowThrottlingService _throttlingService; private readonly ISystemClock _systemClock; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The devices. /// The profile. /// The throttling service. /// The system clock. /// The logger. public DeviceCodeValidator( IDeviceFlowCodeService devices, IProfileService profile, IDeviceFlowThrottlingService throttlingService, ISystemClock systemClock, ILogger logger) { _devices = devices; _profile = profile; _throttlingService = throttlingService; _systemClock = systemClock; _logger = logger; } /// /// Validates the device code. /// /// The context. /// public async Task ValidateAsync(DeviceCodeValidationContext context) { var deviceCode = await _devices.FindByDeviceCodeAsync(context.DeviceCode); if (deviceCode == null) { _logger.LogError("Invalid device code"); context.Result = new TokenRequestValidationResult(context.Request, OidcConstants.TokenErrors.InvalidGrant); return; } // validate client binding if (deviceCode.ClientId != context.Request.Client.ClientId) { _logger.LogError("Client {0} is trying to use a device code from client {1}", context.Request.Client.ClientId, deviceCode.ClientId); context.Result = new TokenRequestValidationResult(context.Request, OidcConstants.TokenErrors.InvalidGrant); return; } if (await _throttlingService.ShouldSlowDown(context.DeviceCode, deviceCode)) { _logger.LogError("Client {0} is polling too fast", deviceCode.ClientId); context.Result = new TokenRequestValidationResult(context.Request, OidcConstants.TokenErrors.SlowDown); return; } // validate lifetime if (deviceCode.CreationTime.AddSeconds(deviceCode.Lifetime) < _systemClock.UtcNow) { _logger.LogError("Expired device code"); context.Result = new TokenRequestValidationResult(context.Request, OidcConstants.TokenErrors.ExpiredToken); return; } // denied if (deviceCode.IsAuthorized && (deviceCode.AuthorizedScopes == null || deviceCode.AuthorizedScopes.Any() == false)) { _logger.LogError("No scopes authorized for device authorization. Access denied"); context.Result = new TokenRequestValidationResult(context.Request, OidcConstants.TokenErrors.AccessDenied); return; } // make sure code is authorized if (!deviceCode.IsAuthorized || deviceCode.Subject == null) { context.Result = new TokenRequestValidationResult(context.Request, OidcConstants.TokenErrors.AuthorizationPending); return; } // make sure user is enabled var isActiveCtx = new IsActiveContext(deviceCode.Subject, context.Request.Client, IdentityServerConstants.ProfileIsActiveCallers.DeviceCodeValidation); await _profile.IsActiveAsync(isActiveCtx); if (isActiveCtx.IsActive == false) { _logger.LogError("User has been disabled: {subjectId}", deviceCode.Subject.GetSubjectId()); context.Result = new TokenRequestValidationResult(context.Request, OidcConstants.TokenErrors.InvalidGrant); return; } context.Request.DeviceCode = deviceCode; context.Request.SessionId = deviceCode.SessionId; context.Result = new TokenRequestValidationResult(context.Request); await _devices.RemoveByDeviceCodeAsync(context.DeviceCode); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/EndSessionRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates requests to the end session endpoint. /// public class EndSessionRequestValidator : IEndSessionRequestValidator { /// /// The logger. /// protected readonly ILogger Logger; /// /// The IdentityServer options. /// protected readonly IdentityServerOptions Options; /// /// The token validator. /// protected readonly ITokenValidator TokenValidator; /// /// The URI validator. /// protected readonly IRedirectUriValidator UriValidator; /// /// The user session service. /// protected readonly IUserSession UserSession; /// /// The logout notification service. /// public ILogoutNotificationService LogoutNotificationService { get; } /// /// The end session message store. /// protected readonly IMessageStore EndSessionMessageStore; /// /// The HTTP context accessor. /// protected readonly IHttpContextAccessor Context; /// /// Creates a new instance of the EndSessionRequestValidator. /// /// /// /// /// /// /// /// /// public EndSessionRequestValidator( IHttpContextAccessor context, IdentityServerOptions options, ITokenValidator tokenValidator, IRedirectUriValidator uriValidator, IUserSession userSession, ILogoutNotificationService logoutNotificationService, IMessageStore endSessionMessageStore, ILogger logger) { Context = context; Options = options; TokenValidator = tokenValidator; UriValidator = uriValidator; UserSession = userSession; LogoutNotificationService = logoutNotificationService; EndSessionMessageStore = endSessionMessageStore; Logger = logger; } /// public async Task ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject) { Logger.LogDebug("Start end session request validation"); var isAuthenticated = subject.IsAuthenticated(); if (!isAuthenticated && Options.Authentication.RequireAuthenticatedUserForSignOutMessage) { return Invalid("User is anonymous. Ignoring end session parameters"); } var validatedRequest = new ValidatedEndSessionRequest { Raw = parameters }; var idTokenHint = parameters.Get(OidcConstants.EndSessionRequest.IdTokenHint); if (idTokenHint.IsPresent()) { // validate id_token - no need to validate token life time var tokenValidationResult = await TokenValidator.ValidateIdentityTokenAsync(idTokenHint, null, false); if (tokenValidationResult.IsError) { return Invalid("Error validating id token hint", validatedRequest); } validatedRequest.Client = tokenValidationResult.Client; // validate sub claim against currently logged on user var subClaim = tokenValidationResult.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.Subject); if (subClaim != null && isAuthenticated) { if (subject.GetSubjectId() != subClaim.Value) { return Invalid("Current user does not match identity token", validatedRequest); } validatedRequest.Subject = subject; validatedRequest.SessionId = await UserSession.GetSessionIdAsync(); validatedRequest.ClientIds = await UserSession.GetClientListAsync(); } var redirectUri = parameters.Get(OidcConstants.EndSessionRequest.PostLogoutRedirectUri); if (redirectUri.IsPresent()) { if (await UriValidator.IsPostLogoutRedirectUriValidAsync(redirectUri, validatedRequest.Client)) { validatedRequest.PostLogOutUri = redirectUri; } else { Logger.LogWarning("Invalid PostLogoutRedirectUri: {postLogoutRedirectUri}", redirectUri); } } if (validatedRequest.PostLogOutUri != null) { var state = parameters.Get(OidcConstants.EndSessionRequest.State); if (state.IsPresent()) { validatedRequest.State = state; } } } else { // no id_token to authenticate the client, but we do have a user and a user session validatedRequest.Subject = subject; validatedRequest.SessionId = await UserSession.GetSessionIdAsync(); validatedRequest.ClientIds = await UserSession.GetClientListAsync(); } LogSuccess(validatedRequest); return new EndSessionValidationResult { ValidatedRequest = validatedRequest, IsError = false }; } /// /// Creates a result that indicates an error. /// /// /// /// protected virtual EndSessionValidationResult Invalid(string message, ValidatedEndSessionRequest request = null) { message = "End session request validation failure: " + message; if (request != null) { var log = new EndSessionRequestValidationLog(request); Logger.LogInformation(message + Environment.NewLine + "{@details}", log); } else { Logger.LogInformation(message); } return new EndSessionValidationResult { IsError = true, Error = "Invalid request", ErrorDescription = message }; } /// /// Logs a success result. /// /// protected virtual void LogSuccess(ValidatedEndSessionRequest request) { var log = new EndSessionRequestValidationLog(request); Logger.LogInformation("End session request validation success" + Environment.NewLine + "{@details}", log); } /// public async Task ValidateCallbackAsync(NameValueCollection parameters) { var result = new EndSessionCallbackValidationResult { IsError = true }; var endSessionId = parameters[Constants.UIConstants.DefaultRoutePathParams.EndSessionCallback]; var endSessionMessage = await EndSessionMessageStore.ReadAsync(endSessionId); if (endSessionMessage?.Data?.ClientIds?.Any() == true) { result.IsError = false; result.FrontChannelLogoutUrls = await LogoutNotificationService.GetFrontChannelLogoutNotificationsUrlsAsync(endSessionMessage.Data); } else { result.Error = "Failed to read end session callback message"; } return result; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/ExtensionGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates an extension grant request using the registered validators /// public class ExtensionGrantValidator { private readonly ILogger _logger; private readonly IEnumerable _validators; /// /// Initializes a new instance of the class. /// /// The validators. /// The logger. public ExtensionGrantValidator(IEnumerable validators, ILogger logger) { if (validators == null) { _validators = Enumerable.Empty(); } else { _validators = validators; } _logger = logger; } /// /// Gets the available grant types. /// /// public IEnumerable GetAvailableGrantTypes() { return _validators.Select(v => v.GrantType); } /// /// Validates the request. /// /// The request. /// public async Task ValidateAsync(ValidatedTokenRequest request) { var validator = _validators.FirstOrDefault(v => v.GrantType.Equals(request.GrantType, StringComparison.Ordinal)); if (validator == null) { _logger.LogError("No validator found for grant type"); return new GrantValidationResult(TokenRequestErrors.UnsupportedGrantType); } try { _logger.LogTrace("Calling into custom grant validator: {type}", validator.GetType().FullName); var context = new ExtensionGrantValidationContext { Request = request }; await validator.ValidateAsync(context); return context.Result; } catch (Exception e) { _logger.LogError(1, e, "Grant validation error: {message}", e.Message); return new GrantValidationResult(TokenRequestErrors.InvalidGrant); } } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/HashedSharedSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Secret = IdentityServer8.Models.Secret; namespace IdentityServer8.Validation; /// /// Validates a shared secret stored in SHA256 or SHA512 /// public class HashedSharedSecretValidator : ISecretValidator { private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The logger. public HashedSharedSecretValidator(ILogger logger) { _logger = logger; } /// /// Validates a secret /// /// The stored secrets. /// The received secret. /// /// A validation result /// /// Id or cedential public Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.SharedSecret) { _logger.LogDebug("Hashed shared secret validator cannot process {type}", parsedSecret.Type ?? "null"); return fail; } var sharedSecrets = secrets.Where(s => s.Type == IdentityServerConstants.SecretTypes.SharedSecret); if (!sharedSecrets.Any()) { _logger.LogDebug("No shared secret configured for client."); return fail; } var sharedSecret = parsedSecret.Credential as string; if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing()) { throw new ArgumentException("Id or Credential is missing."); } var secretSha256 = sharedSecret.Sha256(); var secretSha512 = sharedSecret.Sha512(); foreach (var secret in sharedSecrets) { var secretDescription = string.IsNullOrEmpty(secret.Description) ? "no description" : secret.Description; bool isValid = false; byte[] secretBytes; try { secretBytes = Convert.FromBase64String(secret.Value); } catch (FormatException) { _logger.LogInformation("Secret: {description} uses invalid hashing algorithm.", secretDescription); return fail; } catch (ArgumentNullException) { _logger.LogInformation("Secret: {description} is null.", secretDescription); return fail; } if (secretBytes.Length == 32) { isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha256); } else if (secretBytes.Length == 64) { isValid = TimeConstantComparer.IsEqual(secret.Value, secretSha512); } else { _logger.LogInformation("Secret: {description} uses invalid hashing algorithm.", secretDescription); return fail; } if (isValid) { return success; } } _logger.LogDebug("No matching hashed secret found."); return fail; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/IntrospectionRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// The introspection request validator /// /// internal class IntrospectionRequestValidator : IIntrospectionRequestValidator { private readonly ILogger _logger; private readonly ITokenValidator _tokenValidator; /// /// Initializes a new instance of the class. /// /// The token validator. /// The logger. public IntrospectionRequestValidator(ITokenValidator tokenValidator, ILogger logger) { _tokenValidator = tokenValidator; _logger = logger; } /// /// Validates the request. /// /// The parameters. /// The API. /// public async Task ValidateAsync(NameValueCollection parameters, ApiResource api) { _logger.LogDebug("Introspection request validation started."); // retrieve required token var token = parameters.Get("token"); if (token == null) { _logger.LogError("Token is missing"); return new IntrospectionRequestValidationResult { IsError = true, Api = api, Error = "missing_token", Parameters = parameters }; } // validate token var tokenValidationResult = await _tokenValidator.ValidateAccessTokenAsync(token); // invalid or unknown token if (tokenValidationResult.IsError) { _logger.LogDebug("Token is invalid."); return new IntrospectionRequestValidationResult { IsActive = false, IsError = false, Token = token, Api = api, Parameters = parameters }; } _logger.LogDebug("Introspection request validation successful."); // valid token return new IntrospectionRequestValidationResult { IsActive = true, IsError = false, Token = token, Claims = tokenValidationResult.Claims, Api = api, Parameters = parameters }; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/JwtBearerClientAssertionSecretParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Parses a POST body for a JWT bearer client assertion /// public class JwtBearerClientAssertionSecretParser : ISecretParser { private readonly IdentityServerOptions _options; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The options. /// The logger. public JwtBearerClientAssertionSecretParser(IdentityServerOptions options, ILogger logger) { _options = options; _logger = logger; } /// /// Returns the authentication method name that this parser implements /// /// /// The authentication method. /// public string AuthenticationMethod => OidcConstants.EndpointAuthenticationMethods.PrivateKeyJwt; /// /// Tries to find a JWT client assertion token in the request body that can be used for authentication /// Used for "private_key_jwt" client authentication method as defined in http://openid.net/specs/openid-connect-core-1_0.html#ClientAuthentication /// /// The HTTP context /// /// A parsed secret /// public async Task ParseAsync(HttpContext context) { _logger.LogDebug("Start parsing for JWT client assertion in post body"); if (!context.Request.HasApplicationFormContentType()) { _logger.LogDebug("Content type is not a form"); return null; } var body = await context.Request.ReadFormAsync(); if (body != null) { var clientAssertionType = body[OidcConstants.TokenRequest.ClientAssertionType].FirstOrDefault(); var clientAssertion = body[OidcConstants.TokenRequest.ClientAssertion].FirstOrDefault(); if (clientAssertion.IsPresent() && clientAssertionType == OidcConstants.ClientAssertionTypes.JwtBearer) { if (clientAssertion.Length > _options.InputLengthRestrictions.Jwt) { _logger.LogError("Client assertion token exceeds maximum length."); return null; } var clientId = GetClientIdFromToken(clientAssertion); if (!clientId.IsPresent()) { return null; } if (clientId.Length > _options.InputLengthRestrictions.ClientId) { _logger.LogError("Client ID exceeds maximum length."); return null; } var parsedSecret = new ParsedSecret { Id = clientId, Credential = clientAssertion, Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; return parsedSecret; } } _logger.LogDebug("No JWT client assertion found in post body"); return null; } private string GetClientIdFromToken(string token) { try { var jwt = new JwtSecurityToken(token); return jwt.Subject; } catch (Exception e) { _logger.LogWarning("Could not parse client assertion: {e}", e); return null; } } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/JwtRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Validation; /// /// Validates JWT authorization request objects /// public class JwtRequestValidator { private readonly string _audienceUri; private readonly IHttpContextAccessor _httpContextAccessor; /// /// JWT handler /// protected JwtSecurityTokenHandler Handler = new JwtSecurityTokenHandler { MapInboundClaims = false }; /// /// The audience URI to use /// protected string AudienceUri { get { if (_audienceUri.IsPresent()) { return _audienceUri; } return _httpContextAccessor.HttpContext.GetIdentityServerIssuerUri(); } } /// /// The logger /// protected readonly ILogger Logger; /// /// The optione /// protected readonly IdentityServerOptions Options; /// /// Instantiates an instance of private_key_jwt secret validator /// public JwtRequestValidator(IHttpContextAccessor contextAccessor, IdentityServerOptions options, ILogger logger) { _httpContextAccessor = contextAccessor; Options = options; Logger = logger; } /// /// Instantiates an instance of private_key_jwt secret validator (used for testing) /// internal JwtRequestValidator(string audience, ILogger logger) { _audienceUri = audience; Logger = logger; } /// /// Validates a JWT request object /// /// The client /// The JWT /// public virtual async Task ValidateAsync(Client client, string jwtTokenString) { if (client == null) throw new ArgumentNullException(nameof(client)); if (String.IsNullOrWhiteSpace(jwtTokenString)) throw new ArgumentNullException(nameof(jwtTokenString)); var fail = new JwtRequestValidationResult { IsError = true }; List trustedKeys; try { trustedKeys = await GetKeysAsync(client); } catch (Exception e) { Logger.LogError(e, "Could not parse client secrets"); return fail; } if (!trustedKeys.Any()) { Logger.LogError("There are no keys available to validate JWT."); return fail; } JwtSecurityToken jwtSecurityToken; try { jwtSecurityToken = await ValidateJwtAsync(jwtTokenString, trustedKeys, client); } catch (Exception e) { Logger.LogError(e, "JWT token validation error"); return fail; } if (jwtSecurityToken.Payload.ContainsKey(OidcConstants.AuthorizeRequest.Request) || jwtSecurityToken.Payload.ContainsKey(OidcConstants.AuthorizeRequest.RequestUri)) { Logger.LogError("JWT payload must not contain request or request_uri"); return fail; } var payload = await ProcessPayloadAsync(jwtSecurityToken); var result = new JwtRequestValidationResult { IsError = false, Payload = payload }; Logger.LogDebug("JWT request object validation success."); return result; } /// /// Retrieves keys for a given client /// /// The client /// protected virtual Task> GetKeysAsync(Client client) { return client.ClientSecrets.GetKeysAsync(); } /// /// Validates the JWT token /// /// JWT as a string /// The keys /// The client /// protected virtual Task ValidateJwtAsync(string jwtTokenString, IEnumerable keys, Client client) { var tokenValidationParameters = new TokenValidationParameters { IssuerSigningKeys = keys, ValidateIssuerSigningKey = true, ValidIssuer = client.ClientId, ValidateIssuer = true, ValidAudience = AudienceUri, ValidateAudience = true, RequireSignedTokens = true, RequireExpirationTime = true }; if (Options.StrictJarValidation) { tokenValidationParameters.ValidTypes = new[] { JwtClaimTypes.JwtTypes.AuthorizationRequest }; } Handler.ValidateToken(jwtTokenString, tokenValidationParameters, out var token); return Task.FromResult((JwtSecurityToken)token); } /// /// Processes the JWT contents /// /// The JWT token /// protected virtual Task> ProcessPayloadAsync(JwtSecurityToken token) { // filter JWT validation values var payload = new Dictionary(); foreach (var key in token.Payload.Keys) { if (!Constants.Filters.JwtRequestClaimTypesFilter.Contains(key)) { var value = token.Payload[key]; switch (value) { case string s: payload.Add(key, s); break; case JObject jobj: payload.Add(key, jobj.ToString(Formatting.None)); break; case JArray jarr: payload.Add(key, jarr.ToString(Formatting.None)); break; } } } return Task.FromResult(payload); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/MutualTlsSecretParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Parses secret according to MTLS spec /// public class MutualTlsSecretParser : ISecretParser { private readonly IdentityServerOptions _options; private readonly ILogger _logger; /// /// ctor /// /// /// public MutualTlsSecretParser(IdentityServerOptions options, ILogger logger) { _options = options; _logger = logger; } /// /// Name of authentication method (blank to suppress in discovery since we do special handling) /// public string AuthenticationMethod => String.Empty; /// /// Parses the HTTP context /// /// /// public async Task ParseAsync(HttpContext context) { _logger.LogDebug("Start parsing for client id in post body"); if (!context.Request.HasApplicationFormContentType()) { _logger.LogDebug("Content type is not a form"); return null; } var body = await context.Request.ReadFormAsync(); if (body != null) { var id = body["client_id"].FirstOrDefault(); // client id must be present if (!String.IsNullOrWhiteSpace(id)) { if (id.Length > _options.InputLengthRestrictions.ClientId) { _logger.LogError("Client ID exceeds maximum length."); return null; } var clientCertificate = await context.Connection.GetClientCertificateAsync(); if (clientCertificate is null) { _logger.LogDebug("Client certificate not present"); return null; } return new ParsedSecret { Id = id, Credential = clientCertificate, Type = IdentityServerConstants.ParsedSecretTypes.X509Certificate }; } } _logger.LogDebug("No post body found"); return null; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/NopClientConfigurationValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// No-op client configuration validator (for backwards-compatibility). /// /// public class NopClientConfigurationValidator : IClientConfigurationValidator { /// /// Determines whether the configuration of a client is valid. /// /// The context. /// public Task ValidateAsync(ClientConfigurationValidationContext context) { context.IsValid = true; return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/NotSupportedResouceOwnerCredentialValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default resource owner password validator (no implementation == not supported) /// /// public class NotSupportedResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The logger. public NotSupportedResourceOwnerPasswordValidator(ILogger logger) { _logger = logger; } /// /// Validates the resource owner password credential /// /// The context. /// public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { context.Result = new GrantValidationResult(TokenRequestErrors.UnsupportedGrantType); _logger.LogInformation("Resource owner password credential type not supported. Configure an IResourceOwnerPasswordValidator."); return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/PlainTextSharedSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates a secret stored in plain text /// public class PlainTextSharedSecretValidator : ISecretValidator { private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The logger. public PlainTextSharedSecretValidator(ILogger logger) { _logger = logger; } /// /// Validates a secret /// /// The stored secrets. /// The received secret. /// /// A validation result /// /// id or credential is missing. public Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); var success = Task.FromResult(new SecretValidationResult { Success = true }); if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.SharedSecret) { _logger.LogError("Parsed secret should not be of type: {type}", parsedSecret.Type ?? "null"); return fail; } var sharedSecrets = secrets.Where(s => s.Type == IdentityServerConstants.SecretTypes.SharedSecret); if (!sharedSecrets.Any()) { _logger.LogDebug("No shared secret configured for client."); return fail; } var sharedSecret = parsedSecret.Credential as string; if (parsedSecret.Id.IsMissing() || sharedSecret.IsMissing()) { throw new ArgumentException("Id or Credential is missing."); } foreach (var secret in sharedSecrets) { // use time constant string comparison var isValid = TimeConstantComparer.IsEqual(sharedSecret, secret.Value); if (isValid) { return success; } } _logger.LogDebug("No matching plain text secret found."); return fail; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/PostBodySecretParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Parses a POST body for secrets /// public class PostBodySecretParser : ISecretParser { private readonly ILogger _logger; private readonly IdentityServerOptions _options; /// /// Creates the parser with options /// /// IdentityServer options /// Logger public PostBodySecretParser(IdentityServerOptions options, ILogger logger) { _logger = logger; _options = options; } /// /// Returns the authentication method name that this parser implements /// /// /// The authentication method. /// public string AuthenticationMethod => OidcConstants.EndpointAuthenticationMethods.PostBody; /// /// Tries to find a secret on the context that can be used for authentication /// /// The HTTP context. /// /// A parsed secret /// public async Task ParseAsync(HttpContext context) { _logger.LogDebug("Start parsing for secret in post body"); if (!context.Request.HasApplicationFormContentType()) { _logger.LogDebug("Content type is not a form"); return null; } var body = await context.Request.ReadFormAsync(); if (body != null) { var id = body["client_id"].FirstOrDefault(); var secret = body["client_secret"].FirstOrDefault(); // client id must be present if (id.IsPresent()) { if (id.Length > _options.InputLengthRestrictions.ClientId) { _logger.LogError("Client ID exceeds maximum length."); return null; } if (secret.IsPresent()) { if (secret.Length > _options.InputLengthRestrictions.ClientSecret) { _logger.LogError("Client secret exceeds maximum length."); return null; } return new ParsedSecret { Id = id, Credential = secret, Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; } else { // client secret is optional _logger.LogDebug("client id without secret found"); return new ParsedSecret { Id = id, Type = IdentityServerConstants.ParsedSecretTypes.NoSecret }; } } } _logger.LogDebug("No secret in post body found"); return null; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/PrivateKeyJwtSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Validation; /// /// Validates a secret based on RS256 signed JWT token /// public class PrivateKeyJwtSecretValidator : ISecretValidator { private readonly IHttpContextAccessor _contextAccessor; private readonly IReplayCache _replayCache; private readonly ILogger _logger; private const string Purpose = nameof(PrivateKeyJwtSecretValidator); /// /// Instantiates an instance of private_key_jwt secret validator /// public PrivateKeyJwtSecretValidator(IHttpContextAccessor contextAccessor, IReplayCache replayCache, ILogger logger) { _contextAccessor = contextAccessor; _replayCache = replayCache; _logger = logger; } /// /// Validates a secret /// /// The stored secrets. /// The received secret. /// /// A validation result /// /// ParsedSecret.Credential is not a JWT token public async Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret) { var fail = new SecretValidationResult { Success = false }; var success = new SecretValidationResult { Success = true }; if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.JwtBearer) { return fail; } if (!(parsedSecret.Credential is string jwtTokenString)) { _logger.LogError("ParsedSecret.Credential is not a string."); return fail; } List trustedKeys; try { trustedKeys = await secrets.GetKeysAsync(); } catch (Exception e) { _logger.LogError(e, "Could not parse secrets"); return fail; } if (!trustedKeys.Any()) { _logger.LogError("There are no keys available to validate client assertion."); return fail; } var validAudiences = new[] { // issuer URI (tbd) //_contextAccessor.HttpContext.GetIdentityServerIssuerUri(), // token endpoint URL string.Concat(_contextAccessor.HttpContext.GetIdentityServerIssuerUri().EnsureTrailingSlash(), Constants.ProtocolRoutePaths.Token) }; var tokenValidationParameters = new TokenValidationParameters { IssuerSigningKeys = trustedKeys, ValidateIssuerSigningKey = true, ValidIssuer = parsedSecret.Id, ValidateIssuer = true, ValidAudiences = validAudiences, ValidateAudience = true, RequireSignedTokens = true, RequireExpirationTime = true, ClockSkew = TimeSpan.FromMinutes(5) }; try { var handler = new JwtSecurityTokenHandler(); handler.ValidateToken(jwtTokenString, tokenValidationParameters, out var token); var jwtToken = (JwtSecurityToken)token; if (jwtToken.Subject != jwtToken.Issuer) { _logger.LogError("Both 'sub' and 'iss' in the client assertion token must have a value of client_id."); return fail; } var exp = jwtToken.Payload.Exp; if (!exp.HasValue) { _logger.LogError("exp is missing."); return fail; } var jti = jwtToken.Payload.Jti; if (jti.IsMissing()) { _logger.LogError("jti is missing."); return fail; } if (await _replayCache.ExistsAsync(Purpose, jti)) { _logger.LogError("jti is found in replay cache. Possible replay attack."); return fail; } else { await _replayCache.AddAsync(Purpose, jti, DateTimeOffset.FromUnixTimeSeconds(exp.Value).AddMinutes(5)); } return success; } catch (Exception e) { _logger.LogError(e, "JWT token validation error"); return fail; } } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/ResponseTypeEqualityComparer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Compares resource_type strings, where the order of space-delimited values is insignificant. /// /// /// /// This is to handle the fact that the order of multi-valued response_type lists is /// insignificant, per the OAuth2 spec /// and the /// (OAuth /// 2.0 Multiple Response Type Encoding Practices draft ). /// /// public class ResponseTypeEqualityComparer : IEqualityComparer { /// /// Determines whether the specified values are equal. /// /// The first string to compare. /// The second string to compare. /// true if the specified values are equal; otherwise, false. public bool Equals(string x, string y) { if (x == y) return true; if (x == null || y == null) return false; if (x.Length != y.Length) return false; var xValues = x.Split(' '); var yValues = y.Split(' '); if (xValues.Length != yValues.Length) { return false; } Array.Sort(xValues); Array.Sort(yValues); for (var i = 0; i < xValues.Length; i++) { if (xValues[i] != yValues[i]) { return false; } } return true; } /// /// Returns a hash code for the value. /// /// The value for which a hash code is to be returned. /// A hash code for the value, suitable for use in hashing algorithms and data structures like a hash table. public int GetHashCode(string value) { if (value == null) return 0; var values = value.Split(' '); if (values.Length == 1) { // Only one value, so just spit out the hash code of the whole string return value.GetHashCode(); } Array.Sort(values); // Using Skeet's answer here: http://stackoverflow.com/a/7244729/208990 // Licensed under Creative Commons CC-BY-SA from SO: https://stackoverflow.com/legal/terms-of-service#licensing // Creative Commons CC-BY-SA https://creativecommons.org/licenses/by-sa/4.0/ var hash = 17; foreach (var element in values) { // changed to use StringComparer.Ordinal, rather than StringComparer.InvariantCulture hash = hash * 31 + StringComparer.Ordinal.GetHashCode(element); } return hash; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/SecretParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Uses the registered secret parsers to parse a secret on the current request /// public class SecretParser : ISecretsListParser { private readonly ILogger _logger; private readonly IEnumerable _parsers; /// /// Initializes a new instance of the class. /// /// The parsers. /// The logger. public SecretParser(IEnumerable parsers, ILogger logger) { _parsers = parsers; _logger = logger; } /// /// Checks the context to find a secret. /// /// The HTTP context. /// public async Task ParseAsync(HttpContext context) { // see if a registered parser finds a secret on the request ParsedSecret bestSecret = null; foreach (var parser in _parsers) { var parsedSecret = await parser.ParseAsync(context); if (parsedSecret != null) { _logger.LogDebug("Parser found secret: {type}", parser.GetType().Name); bestSecret = parsedSecret; if (parsedSecret.Type != IdentityServerConstants.ParsedSecretTypes.NoSecret) { break; } } } if (bestSecret != null) { _logger.LogDebug("Secret id found: {id}", Ioc.Sanitizer.Log.Sanitize(bestSecret.Id)); return bestSecret; } _logger.LogDebug("Parser found no secret"); return null; } /// /// Gets all available authentication methods. /// /// public IEnumerable GetAvailableAuthenticationMethods() { return _parsers.Select(p => p.AuthenticationMethod).Where(p => !String.IsNullOrWhiteSpace(p)); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/SecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates secrets using the registered validators /// public class SecretValidator : ISecretsListValidator { private readonly ILogger _logger; private readonly IEnumerable _validators; private readonly ISystemClock _clock; /// /// Initializes a new instance of the class. /// /// The clock. /// The validators. /// The logger. public SecretValidator(ISystemClock clock, IEnumerable validators, ILogger logger) { _clock = clock; _validators = validators; _logger = logger; } /// /// Validates the secret. /// /// The parsed secret. /// The secrets. /// public async Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret) { var secretsArray = secrets as Secret[] ?? secrets.ToArray(); var expiredSecrets = secretsArray.Where(s => s.Expiration.HasExpired(_clock.UtcNow.UtcDateTime)).ToList(); if (expiredSecrets.Any()) { expiredSecrets.ForEach( ex => _logger.LogInformation("Secret [{description}] is expired", ex.Description ?? "no description")); } var currentSecrets = secretsArray.Where(s => !s.Expiration.HasExpired(_clock.UtcNow.UtcDateTime)).ToArray(); // see if a registered validator can validate the secret foreach (var validator in _validators) { var secretValidationResult = await validator.ValidateAsync(currentSecrets, parsedSecret); if (secretValidationResult.Success) { _logger.LogDebug("Secret validator success: {0}", validator.GetType().Name); return secretValidationResult; } } _logger.LogDebug("Secret validators could not validate secret"); return new SecretValidationResult { Success = false }; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/StrictRedirectUriValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default implementation of redirect URI validator. Validates the URIs against /// the client's configured URIs. /// public class StrictRedirectUriValidator : IRedirectUriValidator { /// /// Checks if a given URI string is in a collection of strings (using ordinal ignore case comparison) /// /// The uris. /// The requested URI. /// protected bool StringCollectionContainsString(IEnumerable uris, string requestedUri) { if (uris.EnumerableIsNullOrEmpty()) return false; return uris.Contains(requestedUri, StringComparer.OrdinalIgnoreCase); } /// /// Determines whether a redirect URI is valid for a client. /// /// The requested URI. /// The client. /// /// true is the URI is valid; false otherwise. /// public virtual Task IsRedirectUriValidAsync(string requestedUri, Client client) { return Task.FromResult(StringCollectionContainsString(client.RedirectUris, requestedUri)); } /// /// Determines whether a post logout URI is valid for a client. /// /// The requested URI. /// The client. /// /// true is the URI is valid; false otherwise. /// public virtual Task IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client) { return Task.FromResult(StringCollectionContainsString(client.PostLogoutRedirectUris, requestedUri)); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/StrictRedirectUriValidatorAppAuth.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Implementation of strict redirect URI validator that allows a random port if 127.0.0.1 is used. /// /// public class StrictRedirectUriValidatorAppAuth : StrictRedirectUriValidator { private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The logger. public StrictRedirectUriValidatorAppAuth(ILogger logger) { _logger = logger; } /// /// Determines whether a redirect URI is valid for a client. /// /// The requested URI. /// The client. /// /// true is the URI is valid; false otherwise. /// public override async Task IsRedirectUriValidAsync(string requestedUri, Client client) { var isAllowed = await base.IsRedirectUriValidAsync(requestedUri, client); if (isAllowed) return isAllowed; // since this is appauth specific, we can require pkce if (client.RequirePkce && client.RedirectUris.Contains("http://127.0.0.1")) return IsLoopback(requestedUri); return false; } /// /// Determines whether a post logout URI is valid for a client. /// /// The requested URI. /// The client. /// /// true is the URI is valid; false otherwise. /// public override async Task IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client) { var isAllowed = await base.IsPostLogoutRedirectUriValidAsync(requestedUri, client); if (isAllowed) return isAllowed; // since this is appauth specific, we can require pkce if (client.RequirePkce && client.RedirectUris.Contains("http://127.0.0.1")) return IsLoopback(requestedUri); return false; } /// /// Check if is of the form http://127.0.0.1:port/path. /// /// The requested URI. /// /// true if is a valid Loopback URI; false otherwise. /// internal bool IsLoopback(string requestedUri) { _logger.LogDebug("Checking for 127.0.0.1 redirect URI"); // Validate that the requestedUri is not null or empty. if (string.IsNullOrEmpty(requestedUri)) { _logger.LogDebug("'requestedUri' is null or empty"); return false; } var parts = requestedUri.Split(':'); if (parts.Length != 3) { _logger.LogDebug("invalid format - http://127.0.0.1:port is required."); return false; } if (!string.Equals(parts[0], "http", StringComparison.Ordinal) || !string.Equals(parts[1], "//127.0.0.1", StringComparison.Ordinal)) { _logger.LogDebug("invalid format - http://127.0.0.1:port is required."); return false; } string portAsString; int indexOfPathSeparator = parts[2].IndexOfAny(new[] { '/', '?', '#' }); if (indexOfPathSeparator > 0) { portAsString = parts[2].Substring(0, indexOfPathSeparator); } else { portAsString = parts[2]; } // Valid port range is 0 through 65535. if (int.TryParse(portAsString, out var port)) { if (port >= 0 && port < 65536) { return true; } } _logger.LogDebug("invalid port"); return false; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/TokenRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; internal class TokenRequestValidator : ITokenRequestValidator { private readonly IdentityServerOptions _options; private readonly IAuthorizationCodeStore _authorizationCodeStore; private readonly ExtensionGrantValidator _extensionGrantValidator; private readonly ICustomTokenRequestValidator _customRequestValidator; private readonly IResourceValidator _resourceValidator; private readonly IResourceStore _resourceStore; private readonly ITokenValidator _tokenValidator; private readonly IRefreshTokenService _refreshTokenService; private readonly IEventService _events; private readonly IResourceOwnerPasswordValidator _resourceOwnerValidator; private readonly IProfileService _profile; private readonly IDeviceCodeValidator _deviceCodeValidator; private readonly ISystemClock _clock; private readonly ILogger _logger; private ValidatedTokenRequest _validatedRequest; /// /// Initializes a new instance of the class. /// /// The options. /// The authorization code store. /// The resource owner validator. /// The profile. /// The device code validator. /// The extension grant validator. /// The custom request validator. /// The resource validator. /// The resource store. /// The token validator. /// /// The events. /// The clock. /// The logger. public TokenRequestValidator(IdentityServerOptions options, IAuthorizationCodeStore authorizationCodeStore, IResourceOwnerPasswordValidator resourceOwnerValidator, IProfileService profile, IDeviceCodeValidator deviceCodeValidator, ExtensionGrantValidator extensionGrantValidator, ICustomTokenRequestValidator customRequestValidator, IResourceValidator resourceValidator, IResourceStore resourceStore, ITokenValidator tokenValidator, IRefreshTokenService refreshTokenService, IEventService events, ISystemClock clock, ILogger logger) { _logger = logger; _options = options; _clock = clock; _authorizationCodeStore = authorizationCodeStore; _resourceOwnerValidator = resourceOwnerValidator; _profile = profile; _deviceCodeValidator = deviceCodeValidator; _extensionGrantValidator = extensionGrantValidator; _customRequestValidator = customRequestValidator; _resourceValidator = resourceValidator; _resourceStore = resourceStore; _tokenValidator = tokenValidator; _refreshTokenService = refreshTokenService; _events = events; } /// /// Validates the request. /// /// The parameters. /// The client validation result. /// /// /// parameters /// or /// client /// public async Task ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult) { _logger.LogDebug("Start token request validation"); _validatedRequest = new ValidatedTokenRequest { Raw = parameters ?? throw new ArgumentNullException(nameof(parameters)), Options = _options }; if (clientValidationResult == null) throw new ArgumentNullException(nameof(clientValidationResult)); _validatedRequest.SetClient(clientValidationResult.Client, clientValidationResult.Secret, clientValidationResult.Confirmation); ///////////////////////////////////////////// // check client protocol type ///////////////////////////////////////////// if (_validatedRequest.Client.ProtocolType != IdentityServerConstants.ProtocolTypes.OpenIdConnect) { LogError("Invalid protocol type for client", new { clientId = _validatedRequest.Client.ClientId, expectedProtocolType = IdentityServerConstants.ProtocolTypes.OpenIdConnect, actualProtocolType = _validatedRequest.Client.ProtocolType }); return Invalid(OidcConstants.TokenErrors.InvalidClient); } ///////////////////////////////////////////// // check grant type ///////////////////////////////////////////// var grantType = parameters.Get(OidcConstants.TokenRequest.GrantType); if (grantType.IsMissing()) { LogError("Grant type is missing"); return Invalid(OidcConstants.TokenErrors.UnsupportedGrantType); } if (grantType.Length > _options.InputLengthRestrictions.GrantType) { LogError("Grant type is too long"); return Invalid(OidcConstants.TokenErrors.UnsupportedGrantType); } _validatedRequest.GrantType = grantType; switch (grantType) { case OidcConstants.GrantTypes.AuthorizationCode: return await RunValidationAsync(ValidateAuthorizationCodeRequestAsync, parameters); case OidcConstants.GrantTypes.ClientCredentials: return await RunValidationAsync(ValidateClientCredentialsRequestAsync, parameters); case OidcConstants.GrantTypes.Password: return await RunValidationAsync(ValidateResourceOwnerCredentialRequestAsync, parameters); case OidcConstants.GrantTypes.RefreshToken: return await RunValidationAsync(ValidateRefreshTokenRequestAsync, parameters); case OidcConstants.GrantTypes.DeviceCode: return await RunValidationAsync(ValidateDeviceCodeRequestAsync, parameters); default: return await RunValidationAsync(ValidateExtensionGrantRequestAsync, parameters); } } private async Task RunValidationAsync(Func> validationFunc, NameValueCollection parameters) { // run standard validation var result = await validationFunc(parameters); if (result.IsError) { return result; } // run custom validation _logger.LogTrace("Calling into custom request validator: {type}", _customRequestValidator.GetType().FullName); var customValidationContext = new CustomTokenRequestValidationContext { Result = result }; await _customRequestValidator.ValidateAsync(customValidationContext); if (customValidationContext.Result.IsError) { if (customValidationContext.Result.Error.IsPresent()) { LogError("Custom token request validator", new { error = customValidationContext.Result.Error }); } else { LogError("Custom token request validator error"); } return customValidationContext.Result; } LogSuccess(); return customValidationContext.Result; } private async Task ValidateAuthorizationCodeRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start validation of authorization code token request"); ///////////////////////////////////////////// // check if client is authorized for grant type ///////////////////////////////////////////// if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.AuthorizationCode) && !_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.Hybrid)) { LogError("Client not authorized for code flow"); return Invalid(OidcConstants.TokenErrors.UnauthorizedClient); } ///////////////////////////////////////////// // validate authorization code ///////////////////////////////////////////// var code = parameters.Get(OidcConstants.TokenRequest.Code); if (code.IsMissing()) { LogError("Authorization code is missing"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } if (code.Length > _options.InputLengthRestrictions.AuthorizationCode) { LogError("Authorization code is too long"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } _validatedRequest.AuthorizationCodeHandle = code; var authZcode = await _authorizationCodeStore.GetAuthorizationCodeAsync(code); if (authZcode == null) { LogError("Invalid authorization code", new { code }); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } ///////////////////////////////////////////// // validate client binding ///////////////////////////////////////////// if (authZcode.ClientId != _validatedRequest.Client.ClientId) { LogError("Client is trying to use a code from a different client", new { clientId = _validatedRequest.Client.ClientId, codeClient = authZcode.ClientId }); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } // remove code from store // todo: set to consumed in the future? await _authorizationCodeStore.RemoveAuthorizationCodeAsync(code); if (authZcode.CreationTime.HasExceeded(authZcode.Lifetime, _clock.UtcNow.UtcDateTime)) { LogError("Authorization code expired", new { code }); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } ///////////////////////////////////////////// // populate session id ///////////////////////////////////////////// if (authZcode.SessionId.IsPresent()) { _validatedRequest.SessionId = authZcode.SessionId; } ///////////////////////////////////////////// // validate code expiration ///////////////////////////////////////////// if (authZcode.CreationTime.HasExceeded(_validatedRequest.Client.AuthorizationCodeLifetime, _clock.UtcNow.UtcDateTime)) { LogError("Authorization code is expired"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } _validatedRequest.AuthorizationCode = authZcode; _validatedRequest.Subject = authZcode.Subject; ///////////////////////////////////////////// // validate redirect_uri ///////////////////////////////////////////// var redirectUri = parameters.Get(OidcConstants.TokenRequest.RedirectUri); if (redirectUri.IsMissing()) { LogError("Redirect URI is missing"); return Invalid(OidcConstants.TokenErrors.UnauthorizedClient); } if (redirectUri.Equals(_validatedRequest.AuthorizationCode.RedirectUri, StringComparison.Ordinal) == false) { LogError("Invalid redirect_uri", new { redirectUri, expectedRedirectUri = _validatedRequest.AuthorizationCode.RedirectUri }); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } ///////////////////////////////////////////// // validate scopes are present ///////////////////////////////////////////// if (_validatedRequest.AuthorizationCode.RequestedScopes == null || !_validatedRequest.AuthorizationCode.RequestedScopes.Any()) { LogError("Authorization code has no associated scopes"); return Invalid(OidcConstants.TokenErrors.InvalidRequest); } ///////////////////////////////////////////// // validate PKCE parameters ///////////////////////////////////////////// var codeVerifier = parameters.Get(OidcConstants.TokenRequest.CodeVerifier); if (_validatedRequest.Client.RequirePkce || _validatedRequest.AuthorizationCode.CodeChallenge.IsPresent()) { _logger.LogDebug("Client required a proof key for code exchange. Starting PKCE validation"); var proofKeyResult = ValidateAuthorizationCodeWithProofKeyParameters(codeVerifier, _validatedRequest.AuthorizationCode); if (proofKeyResult.IsError) { return proofKeyResult; } _validatedRequest.CodeVerifier = codeVerifier; } else { if (codeVerifier.IsPresent()) { LogError("Unexpected code_verifier: {codeVerifier}. This happens when the client is trying to use PKCE, but it is not enabled. Set RequirePkce to true.", codeVerifier); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } } ///////////////////////////////////////////// // make sure user is enabled ///////////////////////////////////////////// var isActiveCtx = new IsActiveContext(_validatedRequest.AuthorizationCode.Subject, _validatedRequest.Client, IdentityServerConstants.ProfileIsActiveCallers.AuthorizationCodeValidation); await _profile.IsActiveAsync(isActiveCtx); if (isActiveCtx.IsActive == false) { LogError("User has been disabled", new { subjectId = _validatedRequest.AuthorizationCode.Subject.GetSubjectId() }); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } _logger.LogDebug("Validation of authorization code token request success"); return Valid(); } private async Task ValidateClientCredentialsRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start client credentials token request validation"); ///////////////////////////////////////////// // check if client is authorized for grant type ///////////////////////////////////////////// if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.ClientCredentials)) { LogError("Client not authorized for client credentials flow, check the AllowedGrantTypes setting", new { clientId = _validatedRequest.Client.ClientId }); return Invalid(OidcConstants.TokenErrors.UnauthorizedClient); } ///////////////////////////////////////////// // check if client is allowed to request scopes ///////////////////////////////////////////// if (!await ValidateRequestedScopesAsync(parameters, ignoreImplicitIdentityScopes: true, ignoreImplicitOfflineAccess: true)) { return Invalid(OidcConstants.TokenErrors.InvalidScope); } if (_validatedRequest.ValidatedResources.Resources.IdentityResources.Any()) { LogError("Client cannot request OpenID scopes in client credentials flow", new { clientId = _validatedRequest.Client.ClientId }); return Invalid(OidcConstants.TokenErrors.InvalidScope); } if (_validatedRequest.ValidatedResources.Resources.OfflineAccess) { LogError("Client cannot request a refresh token in client credentials flow", new { clientId = _validatedRequest.Client.ClientId }); return Invalid(OidcConstants.TokenErrors.InvalidScope); } _logger.LogDebug("{clientId} credentials token request validation success", _validatedRequest.Client.ClientId); return Valid(); } private async Task ValidateResourceOwnerCredentialRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start resource owner password token request validation"); ///////////////////////////////////////////// // check if client is authorized for grant type ///////////////////////////////////////////// if (!_validatedRequest.Client.AllowedGrantTypes.Contains(GrantType.ResourceOwnerPassword)) { LogError("Client not authorized for resource owner flow, check the AllowedGrantTypes setting", new { client_id = _validatedRequest.Client.ClientId }); return Invalid(OidcConstants.TokenErrors.UnauthorizedClient); } ///////////////////////////////////////////// // check if client is allowed to request scopes ///////////////////////////////////////////// if (!(await ValidateRequestedScopesAsync(parameters))) { return Invalid(OidcConstants.TokenErrors.InvalidScope); } ///////////////////////////////////////////// // check resource owner credentials ///////////////////////////////////////////// var userName = parameters.Get(OidcConstants.TokenRequest.UserName); var password = parameters.Get(OidcConstants.TokenRequest.Password); if (userName.IsMissing()) { LogError("Username is missing"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } if (password.IsMissing()) { password = ""; } if (userName.Length > _options.InputLengthRestrictions.UserName || password.Length > _options.InputLengthRestrictions.Password) { LogError("Username or password too long"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } _validatedRequest.UserName = userName; ///////////////////////////////////////////// // authenticate user ///////////////////////////////////////////// var resourceOwnerContext = new ResourceOwnerPasswordValidationContext { UserName = userName, Password = password, Request = _validatedRequest }; await _resourceOwnerValidator.ValidateAsync(resourceOwnerContext); if (resourceOwnerContext.Result.IsError) { // protect against bad validator implementations resourceOwnerContext.Result.Error ??= OidcConstants.TokenErrors.InvalidGrant; if (resourceOwnerContext.Result.Error == OidcConstants.TokenErrors.UnsupportedGrantType) { LogError("Resource owner password credential grant type not supported"); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, "password grant type not supported", resourceOwnerContext.Request.Client.ClientId); return Invalid(OidcConstants.TokenErrors.UnsupportedGrantType, customResponse: resourceOwnerContext.Result.CustomResponse); } var errorDescription = "invalid_username_or_password"; if (resourceOwnerContext.Result.ErrorDescription.IsPresent()) { errorDescription = resourceOwnerContext.Result.ErrorDescription; } LogInformation("User authentication failed: ", errorDescription ?? resourceOwnerContext.Result.Error); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, errorDescription, resourceOwnerContext.Request.Client.ClientId); return Invalid(resourceOwnerContext.Result.Error, errorDescription, resourceOwnerContext.Result.CustomResponse); } if (resourceOwnerContext.Result.Subject == null) { var error = "User authentication failed: no principal returned"; LogError(error); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, error, resourceOwnerContext.Request.Client.ClientId); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } ///////////////////////////////////////////// // make sure user is enabled ///////////////////////////////////////////// var isActiveCtx = new IsActiveContext(resourceOwnerContext.Result.Subject, _validatedRequest.Client, IdentityServerConstants.ProfileIsActiveCallers.ResourceOwnerValidation); await _profile.IsActiveAsync(isActiveCtx); if (isActiveCtx.IsActive == false) { LogError("User has been disabled", new { subjectId = resourceOwnerContext.Result.Subject.GetSubjectId() }); await RaiseFailedResourceOwnerAuthenticationEventAsync(userName, "user is inactive", resourceOwnerContext.Request.Client.ClientId); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } _validatedRequest.UserName = userName; _validatedRequest.Subject = resourceOwnerContext.Result.Subject; await RaiseSuccessfulResourceOwnerAuthenticationEventAsync(userName, resourceOwnerContext.Result.Subject.GetSubjectId(), resourceOwnerContext.Request.Client.ClientId); _logger.LogDebug("Resource owner password token request validation success."); return Valid(resourceOwnerContext.Result.CustomResponse); } private async Task ValidateRefreshTokenRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start validation of refresh token request"); var refreshTokenHandle = parameters.Get(OidcConstants.TokenRequest.RefreshToken); if (refreshTokenHandle.IsMissing()) { LogError("Refresh token is missing"); return Invalid(OidcConstants.TokenErrors.InvalidRequest); } if (refreshTokenHandle.Length > _options.InputLengthRestrictions.RefreshToken) { LogError("Refresh token too long"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } var result = await _refreshTokenService.ValidateRefreshTokenAsync(refreshTokenHandle, _validatedRequest.Client); if (result.IsError) { LogWarning("Refresh token validation failed. aborting"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } _validatedRequest.RefreshToken = result.RefreshToken; _validatedRequest.RefreshTokenHandle = refreshTokenHandle; _validatedRequest.Subject = result.RefreshToken.Subject; _logger.LogDebug("Validation of refresh token request success"); // todo: more logging - similar to TokenValidator before return Valid(); } private async Task ValidateDeviceCodeRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start validation of device code request"); ///////////////////////////////////////////// // check if client is authorized for grant type ///////////////////////////////////////////// if (!_validatedRequest.Client.AllowedGrantTypes.ToList().Contains(GrantType.DeviceFlow)) { LogError("Client not authorized for device flow"); return Invalid(OidcConstants.TokenErrors.UnauthorizedClient); } ///////////////////////////////////////////// // validate device code parameter ///////////////////////////////////////////// var deviceCode = parameters.Get(OidcConstants.TokenRequest.DeviceCode); if (deviceCode.IsMissing()) { LogError("Device code is missing"); return Invalid(OidcConstants.TokenErrors.InvalidRequest); } if (deviceCode.Length > _options.InputLengthRestrictions.DeviceCode) { LogError("Device code too long"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } ///////////////////////////////////////////// // validate device code ///////////////////////////////////////////// var deviceCodeContext = new DeviceCodeValidationContext { DeviceCode = deviceCode, Request = _validatedRequest }; await _deviceCodeValidator.ValidateAsync(deviceCodeContext); if (deviceCodeContext.Result.IsError) return deviceCodeContext.Result; _logger.LogDebug("Validation of authorization code token request success"); return Valid(); } private async Task ValidateExtensionGrantRequestAsync(NameValueCollection parameters) { _logger.LogDebug("Start validation of custom grant token request"); ///////////////////////////////////////////// // check if client is allowed to use grant type ///////////////////////////////////////////// if (!_validatedRequest.Client.AllowedGrantTypes.Contains(_validatedRequest.GrantType)) { LogError("Client does not have the custom grant type in the allowed list, therefore requested grant is not allowed", new { clientId = _validatedRequest.Client.ClientId }); return Invalid(OidcConstants.TokenErrors.UnsupportedGrantType); } ///////////////////////////////////////////// // check if a validator is registered for the grant type ///////////////////////////////////////////// if (!_extensionGrantValidator.GetAvailableGrantTypes().Contains(_validatedRequest.GrantType, StringComparer.Ordinal)) { LogError("No validator is registered for the grant type", new { grantType = _validatedRequest.GrantType }); return Invalid(OidcConstants.TokenErrors.UnsupportedGrantType); } ///////////////////////////////////////////// // check if client is allowed to request scopes ///////////////////////////////////////////// if (!await ValidateRequestedScopesAsync(parameters)) { return Invalid(OidcConstants.TokenErrors.InvalidScope); } ///////////////////////////////////////////// // validate custom grant type ///////////////////////////////////////////// var result = await _extensionGrantValidator.ValidateAsync(_validatedRequest); if (result == null) { LogError("Invalid extension grant"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } if (result.IsError) { if (result.Error.IsPresent()) { LogError("Invalid extension grant", new { error = result.Error }); return Invalid(result.Error, result.ErrorDescription, result.CustomResponse); } else { LogError("Invalid extension grant"); return Invalid(OidcConstants.TokenErrors.InvalidGrant, customResponse: result.CustomResponse); } } if (result.Subject != null) { ///////////////////////////////////////////// // make sure user is enabled ///////////////////////////////////////////// var isActiveCtx = new IsActiveContext( result.Subject, _validatedRequest.Client, IdentityServerConstants.ProfileIsActiveCallers.ExtensionGrantValidation); await _profile.IsActiveAsync(isActiveCtx); if (isActiveCtx.IsActive == false) { // todo: raise event? LogError("User has been disabled", new { subjectId = result.Subject.GetSubjectId() }); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } _validatedRequest.Subject = result.Subject; } _logger.LogDebug("Validation of extension grant token request success"); return Valid(result.CustomResponse); } // todo: do we want to rework the semantics of these ignore params? // also seems like other workflows other than CC clients can omit scopes? private async Task ValidateRequestedScopesAsync(NameValueCollection parameters, bool ignoreImplicitIdentityScopes = false, bool ignoreImplicitOfflineAccess = false) { var scopes = parameters.Get(OidcConstants.TokenRequest.Scope); if (scopes.IsMissing()) { _logger.LogTrace("Client provided no scopes - checking allowed scopes list"); if (!_validatedRequest.Client.AllowedScopes.EnumerableIsNullOrEmpty()) { // this finds all the scopes the client is allowed to access var clientAllowedScopes = new List(); if (!ignoreImplicitIdentityScopes) { var resources = await _resourceStore.FindResourcesByScopeAsync(_validatedRequest.Client.AllowedScopes); clientAllowedScopes.AddRange(resources.ToScopeNames().Where(x => _validatedRequest.Client.AllowedScopes.Contains(x))); } else { var apiScopes = await _resourceStore.FindApiScopesByNameAsync(_validatedRequest.Client.AllowedScopes); clientAllowedScopes.AddRange(apiScopes.Select(x => x.Name)); } if (!ignoreImplicitOfflineAccess) { if (_validatedRequest.Client.AllowOfflineAccess) { clientAllowedScopes.Add(IdentityServerConstants.StandardScopes.OfflineAccess); } } scopes = clientAllowedScopes.Distinct().ToSpaceSeparatedString(); _logger.LogTrace("Defaulting to: {scopes}", scopes); } else { LogError("No allowed scopes configured for client", new { clientId = _validatedRequest.Client.ClientId }); return false; } } if (scopes.Length > _options.InputLengthRestrictions.Scope) { LogError("Scope parameter exceeds max allowed length"); return false; } var requestedScopes = scopes.ParseScopesString(); if (requestedScopes == null) { LogError("No scopes found in request"); return false; } var resourceValidationResult = await _resourceValidator.ValidateRequestedResourcesAsync(new ResourceValidationRequest { Client = _validatedRequest.Client, Scopes = requestedScopes }); if (!resourceValidationResult.Succeeded) { if (resourceValidationResult.InvalidScopes.Any()) { LogError("Invalid scopes requested"); } else { LogError("Invalid scopes for client requested"); } return false; } _validatedRequest.RequestedScopes = requestedScopes; _validatedRequest.ValidatedResources = resourceValidationResult; return true; } private TokenRequestValidationResult ValidateAuthorizationCodeWithProofKeyParameters(string codeVerifier, AuthorizationCode authZcode) { if (authZcode.CodeChallenge.IsMissing() || authZcode.CodeChallengeMethod.IsMissing()) { LogError("Client is missing code challenge or code challenge method", new { clientId = _validatedRequest.Client.ClientId }); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } if (codeVerifier.IsMissing()) { LogError("Missing code_verifier"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } if (codeVerifier.Length < _options.InputLengthRestrictions.CodeVerifierMinLength || codeVerifier.Length > _options.InputLengthRestrictions.CodeVerifierMaxLength) { LogError("code_verifier is too short or too long"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } if (Constants.SupportedCodeChallengeMethods.Contains(authZcode.CodeChallengeMethod) == false) { LogError("Unsupported code challenge method", new { codeChallengeMethod = authZcode.CodeChallengeMethod }); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } if (ValidateCodeVerifierAgainstCodeChallenge(codeVerifier, authZcode.CodeChallenge, authZcode.CodeChallengeMethod) == false) { LogError("Transformed code verifier does not match code challenge"); return Invalid(OidcConstants.TokenErrors.InvalidGrant); } return Valid(); } private bool ValidateCodeVerifierAgainstCodeChallenge(string codeVerifier, string codeChallenge, string codeChallengeMethod) { if (codeChallengeMethod == OidcConstants.CodeChallengeMethods.Plain) { return TimeConstantComparer.IsEqual(codeVerifier.Sha256(), codeChallenge); } var codeVerifierBytes = Encoding.ASCII.GetBytes(codeVerifier); var hashedBytes = codeVerifierBytes.Sha256(); var transformedCodeVerifier = Base64Url.Encode(hashedBytes); return TimeConstantComparer.IsEqual(transformedCodeVerifier.Sha256(), codeChallenge); } private TokenRequestValidationResult Valid(Dictionary customResponse = null) { return new TokenRequestValidationResult(_validatedRequest, customResponse); } private TokenRequestValidationResult Invalid(string error, string errorDescription = null, Dictionary customResponse = null) { return new TokenRequestValidationResult(_validatedRequest, error, errorDescription, customResponse); } private void LogError(string message = null, object values = null) { LogWithRequestDetails(LogLevel.Error, message, values); } private void LogWarning(string message = null, object values = null) { LogWithRequestDetails(LogLevel.Warning, message, values); } private void LogInformation(string message = null, object values = null) { LogWithRequestDetails(LogLevel.Information, message, values); } private void LogWithRequestDetails(LogLevel logLevel, string message = null, object values = null) { var details = new TokenRequestValidationLog(_validatedRequest, _options.Logging.TokenRequestSensitiveValuesFilter); if (message.IsPresent()) { try { if (values == null) { _logger.Log(logLevel, message + ", {@details}", details); } else { _logger.Log(logLevel, message + "{@values}, details: {@details}", values, details); } } catch (Exception ex) { _logger.LogError("Error logging {exception}, request details: {@details}", ex.Message, details); } } else { _logger.Log(logLevel, "{@details}", details); } } private void LogSuccess() { LogWithRequestDetails(LogLevel.Information, "Token request validation success"); } private Task RaiseSuccessfulResourceOwnerAuthenticationEventAsync(string userName, string subjectId, string clientId) { return _events.RaiseAsync(new UserLoginSuccessEvent(userName, subjectId, null, interactive: false, clientId)); } private Task RaiseFailedResourceOwnerAuthenticationEventAsync(string userName, string error, string clientId) { return _events.RaiseAsync(new UserLoginFailureEvent(userName, error, interactive: false, clientId: clientId)); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/TokenRevocationRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// The token revocation request validator /// /// internal class TokenRevocationRequestValidator : ITokenRevocationRequestValidator { private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The logger. public TokenRevocationRequestValidator(ILogger logger) { _logger = logger; } /// /// Validates the request. /// /// The parameters. /// The client. /// /// /// parameters /// or /// client /// public Task ValidateRequestAsync(NameValueCollection parameters, Client client) { _logger.LogTrace("ValidateRequestAsync called"); if (parameters == null) { _logger.LogError("no parameters passed"); throw new ArgumentNullException(nameof(parameters)); } if (client == null) { _logger.LogError("no client passed"); throw new ArgumentNullException(nameof(client)); } //////////////////////////// // make sure token is present /////////////////////////// var token = parameters.Get("token"); if (token.IsMissing()) { _logger.LogError("No token found in request"); return Task.FromResult(new TokenRevocationRequestValidationResult { IsError = true, Error = OidcConstants.TokenErrors.InvalidRequest }); } var result = new TokenRevocationRequestValidationResult { IsError = false, Token = token, Client = client }; //////////////////////////// // check token type hint /////////////////////////// var hint = parameters.Get("token_type_hint"); if (hint.IsPresent()) { if (Constants.SupportedTokenTypeHints.Contains(hint)) { _logger.LogDebug("Token type hint found in request: {tokenTypeHint}", hint); result.TokenTypeHint = hint; } else { _logger.LogError("Invalid token type hint: {tokenTypeHint}", hint); result.IsError = true; result.Error = Constants.RevocationErrors.UnsupportedTokenType; } } _logger.LogDebug("ValidateRequestAsync result: {validateRequestResult}", result); return Task.FromResult(result); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/TokenValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.IdentityModel.Tokens; namespace IdentityServer8.Validation; internal class TokenValidator : ITokenValidator { private readonly ILogger _logger; private readonly IdentityServerOptions _options; private readonly IHttpContextAccessor _context; private readonly IReferenceTokenStore _referenceTokenStore; private readonly ICustomTokenValidator _customValidator; private readonly IClientStore _clients; private readonly IProfileService _profile; private readonly IKeyMaterialService _keys; private readonly ISystemClock _clock; private readonly TokenValidationLog _log; public TokenValidator( IdentityServerOptions options, IHttpContextAccessor context, IClientStore clients, IProfileService profile, IReferenceTokenStore referenceTokenStore, IRefreshTokenStore refreshTokenStore, ICustomTokenValidator customValidator, IKeyMaterialService keys, ISystemClock clock, ILogger logger) { _options = options; _context = context; _clients = clients; _profile = profile; _referenceTokenStore = referenceTokenStore; _customValidator = customValidator; _keys = keys; _clock = clock; _logger = logger; _log = new TokenValidationLog(); } public async Task ValidateIdentityTokenAsync(string token, string clientId = null, bool validateLifetime = true) { _logger.LogDebug("Start identity token validation"); if (token.Length > _options.InputLengthRestrictions.Jwt) { _logger.LogError("JWT too long"); return Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken); } if (clientId.IsMissing()) { clientId = GetClientIdFromJwt(token); if (clientId.IsMissing()) { _logger.LogError("No clientId supplied, can't find id in identity token."); return Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken); } } _log.ClientId = clientId; _log.ValidateLifetime = validateLifetime; var client = await _clients.FindEnabledClientByIdAsync(clientId); if (client == null) { _logger.LogError("Unknown or disabled client: {clientId}.", clientId); return Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken); } _log.ClientName = client.ClientName; _logger.LogDebug("Client found: {clientId} / {clientName}", client.ClientId, client.ClientName); var keys = await _keys.GetValidationKeysAsync(); var result = await ValidateJwtAsync(token, keys, audience: clientId, validateLifetime: validateLifetime); result.Client = client; if (result.IsError) { LogError("Error validating JWT"); return result; } _logger.LogDebug("Calling into custom token validator: {type}", _customValidator.GetType().FullName); var customResult = await _customValidator.ValidateIdentityTokenAsync(result); if (customResult.IsError) { LogError("Custom validator failed: " + (customResult.Error ?? "unknown")); return customResult; } _log.Claims = customResult.Claims.ToClaimsDictionary(); LogSuccess(); return customResult; } public async Task ValidateAccessTokenAsync(string token, string expectedScope = null) { _logger.LogTrace("Start access token validation"); _log.ExpectedScope = expectedScope; _log.ValidateLifetime = true; TokenValidationResult result; if (token.Contains(".")) { if (token.Length > _options.InputLengthRestrictions.Jwt) { _logger.LogError("JWT too long"); return new TokenValidationResult { IsError = true, Error = OidcConstants.ProtectedResourceErrors.InvalidToken, ErrorDescription = "Token too long" }; } _log.AccessTokenType = AccessTokenType.Jwt.ToString(); result = await ValidateJwtAsync( token, await _keys.GetValidationKeysAsync()); } else { if (token.Length > _options.InputLengthRestrictions.TokenHandle) { _logger.LogError("token handle too long"); return new TokenValidationResult { IsError = true, Error = OidcConstants.ProtectedResourceErrors.InvalidToken, ErrorDescription = "Token too long" }; } _log.AccessTokenType = AccessTokenType.Reference.ToString(); result = await ValidateReferenceAccessTokenAsync(token); } _log.Claims = result.Claims.ToClaimsDictionary(); if (result.IsError) { return result; } // make sure client is still active (if client_id claim is present) var clientClaim = result.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.ClientId); if (clientClaim != null) { var client = await _clients.FindEnabledClientByIdAsync(clientClaim.Value); if (client == null) { _logger.LogError("Client deleted or disabled: {clientId}", clientClaim.Value); result.IsError = true; result.Error = OidcConstants.ProtectedResourceErrors.InvalidToken; result.Claims = null; return result; } } // make sure user is still active (if sub claim is present) var subClaim = result.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.Subject); if (subClaim != null) { var principal = Principal.Create("tokenvalidator", result.Claims.ToArray()); if (result.ReferenceTokenId.IsPresent()) { principal.Identities.First().AddClaim(new Claim(JwtClaimTypes.ReferenceTokenId, result.ReferenceTokenId)); } var isActiveCtx = new IsActiveContext(principal, result.Client, IdentityServerConstants.ProfileIsActiveCallers.AccessTokenValidation); await _profile.IsActiveAsync(isActiveCtx); if (isActiveCtx.IsActive == false) { _logger.LogError("User marked as not active: {subject}", subClaim.Value); result.IsError = true; result.Error = OidcConstants.ProtectedResourceErrors.InvalidToken; result.Claims = null; return result; } } // check expected scope(s) if (expectedScope.IsPresent()) { var scope = result.Claims.FirstOrDefault(c => c.Type == JwtClaimTypes.Scope && c.Value == expectedScope); if (scope == null) { LogError($"Checking for expected scope {expectedScope} failed"); return Invalid(OidcConstants.ProtectedResourceErrors.InsufficientScope); } } _logger.LogDebug("Calling into custom token validator: {type}", _customValidator.GetType().FullName); var customResult = await _customValidator.ValidateAccessTokenAsync(result); if (customResult.IsError) { LogError("Custom validator failed: " + (customResult.Error ?? "unknown")); return customResult; } // add claims again after custom validation _log.Claims = customResult.Claims.ToClaimsDictionary(); LogSuccess(); return customResult; } private async Task ValidateJwtAsync(string jwt, IEnumerable validationKeys, bool validateLifetime = true, string audience = null) { var handler = new JwtSecurityTokenHandler(); handler.InboundClaimTypeMap.Clear(); var parameters = new TokenValidationParameters { ValidIssuer = _context.HttpContext.GetIdentityServerIssuerUri(), IssuerSigningKeys = validationKeys.Select(k => k.Key), ValidateLifetime = validateLifetime }; if (audience.IsPresent()) { parameters.ValidAudience = audience; } else { parameters.ValidateAudience = false; } try { var id = handler.ValidateToken(jwt, parameters, out var securityToken); var jwtSecurityToken = securityToken as JwtSecurityToken; // if no audience is specified, we make at least sure that it is an access token if (audience.IsMissing()) { if (_options.AccessTokenJwtType.IsPresent()) { var type = jwtSecurityToken.Header.Typ; if (!string.Equals(type, _options.AccessTokenJwtType)) { return new TokenValidationResult { IsError = true, Error = "invalid JWT token type" }; } } } // if access token contains an ID, log it var jwtId = id.FindFirst(JwtClaimTypes.JwtId); if (jwtId != null) { _log.JwtId = jwtId.Value; } // load the client that belongs to the client_id claim Client client = null; var clientId = id.FindFirst(JwtClaimTypes.ClientId); if (clientId != null) { client = await _clients.FindEnabledClientByIdAsync(clientId.Value); if (client == null) { throw new InvalidOperationException("Client does not exist anymore."); } } var claims = id.Claims.ToList(); // check the scope format (array vs space delimited string) var scopes = claims.Where(c => c.Type == JwtClaimTypes.Scope).ToArray(); if (scopes.Any()) { foreach (var scope in scopes) { if (scope.Value.Contains(" ")) { claims.Remove(scope); var values = scope.Value.Split(' ', StringSplitOptions.RemoveEmptyEntries); foreach (var value in values) { claims.Add(new Claim(JwtClaimTypes.Scope, value)); } } } } return new TokenValidationResult { IsError = false, Claims = claims, Client = client, Jwt = jwt }; } catch (SecurityTokenExpiredException expiredException) { _logger.LogInformation(expiredException, "JWT token validation error: {exception}", expiredException.Message); return Invalid(OidcConstants.ProtectedResourceErrors.ExpiredToken); } catch (Exception ex) { _logger.LogError(ex, "JWT token validation error: {exception}", ex.Message); return Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken); } } private async Task ValidateReferenceAccessTokenAsync(string tokenHandle) { _log.TokenHandle = tokenHandle; var token = await _referenceTokenStore.GetReferenceTokenAsync(tokenHandle); if (token == null) { LogError("Invalid reference token."); return Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken); } if (token.CreationTime.HasExceeded(token.Lifetime, _clock.UtcNow.UtcDateTime)) { LogError("Token expired."); await _referenceTokenStore.RemoveReferenceTokenAsync(tokenHandle); return Invalid(OidcConstants.ProtectedResourceErrors.ExpiredToken); } // load the client that is defined in the token Client client = null; if (token.ClientId != null) { client = await _clients.FindEnabledClientByIdAsync(token.ClientId); } if (client == null) { LogError($"Client deleted or disabled: {token.ClientId}"); return Invalid(OidcConstants.ProtectedResourceErrors.InvalidToken); } return new TokenValidationResult { IsError = false, Client = client, Claims = ReferenceTokenToClaims(token), ReferenceToken = token, ReferenceTokenId = tokenHandle }; } private IEnumerable ReferenceTokenToClaims(Token token) { var claims = new List { new Claim(JwtClaimTypes.Issuer, token.Issuer), new Claim(JwtClaimTypes.NotBefore, new DateTimeOffset(token.CreationTime).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64), new Claim(JwtClaimTypes.Expiration, new DateTimeOffset(token.CreationTime).AddSeconds(token.Lifetime).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) }; foreach (var aud in token.Audiences) { claims.Add(new Claim(JwtClaimTypes.Audience, aud)); } claims.AddRange(token.Claims); return claims; } private string GetClientIdFromJwt(string token) { try { var jwt = new JwtSecurityToken(token); var clientId = jwt.Audiences.FirstOrDefault(); return clientId; } catch (Exception ex) { _logger.LogError(ex, "Malformed JWT token: {exception}", ex.Message); return null; } } private TokenValidationResult Invalid(string error) { return new TokenValidationResult { IsError = true, Error = error }; } private void LogError(string message) { _logger.LogError(message + "\n{@logMessage}", _log); } private void LogSuccess() { _logger.LogDebug("Token validation success\n{@logMessage}", _log); } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/UserInfoRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Default userinfo request validator /// /// internal class UserInfoRequestValidator : IUserInfoRequestValidator { private readonly ITokenValidator _tokenValidator; private readonly IProfileService _profile; private readonly ILogger _logger; /// /// Initializes a new instance of the class. /// /// The token validator. /// The profile service /// The logger. public UserInfoRequestValidator( ITokenValidator tokenValidator, IProfileService profile, ILogger logger) { _tokenValidator = tokenValidator; _profile = profile; _logger = logger; } /// /// Validates a userinfo request. /// /// The access token. /// /// public async Task ValidateRequestAsync(string accessToken) { // the access token needs to be valid and have at least the openid scope var tokenResult = await _tokenValidator.ValidateAccessTokenAsync( accessToken, IdentityServerConstants.StandardScopes.OpenId); if (tokenResult.IsError) { return new UserInfoRequestValidationResult { IsError = true, Error = tokenResult.Error }; } // the token must have a one sub claim var subClaim = tokenResult.Claims.SingleOrDefault(c => c.Type == JwtClaimTypes.Subject); if (subClaim == null) { _logger.LogError("Token contains no sub claim"); return new UserInfoRequestValidationResult { IsError = true, Error = OidcConstants.ProtectedResourceErrors.InvalidToken }; } // create subject from incoming access token var claims = tokenResult.Claims.Where(x => !Constants.Filters.ProtocolClaimsFilter.Contains(x.Type)); var subject = Principal.Create("UserInfo", claims.ToArray()); // make sure user is still active var isActiveContext = new IsActiveContext(subject, tokenResult.Client, IdentityServerConstants.ProfileIsActiveCallers.UserInfoRequestValidation); await _profile.IsActiveAsync(isActiveContext); if (isActiveContext.IsActive == false) { _logger.LogError("User is not active: {sub}", subject.GetSubjectId()); return new UserInfoRequestValidationResult { IsError = true, Error = OidcConstants.ProtectedResourceErrors.InvalidToken }; } return new UserInfoRequestValidationResult { IsError = false, TokenValidationResult = tokenResult, Subject = subject }; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/X509NameSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validator for an X.509 certificate based client secret using the common name /// public class X509NameSecretValidator : ISecretValidator { private readonly ILogger _logger; /// /// ctor /// /// public X509NameSecretValidator(ILogger logger) { _logger = logger; } /// public Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); if (parsedSecret.Type != ParsedSecretTypes.X509Certificate) { _logger.LogDebug("X509 name secret validator cannot process {type}", parsedSecret.Type ?? "null"); return fail; } if (!(parsedSecret.Credential is X509Certificate2 cert)) { throw new InvalidOperationException("Credential is not a x509 certificate."); } var name = cert.Subject; if (name == null) { _logger.LogWarning("No subject/name found in X509 certificate."); return fail; } var nameSecrets = secrets.Where(s => s.Type == SecretTypes.X509CertificateName); if (!nameSecrets.Any()) { _logger.LogDebug("No x509 name secrets configured for client."); return fail; } foreach (var nameSecret in nameSecrets) { if (name.Equals(nameSecret.Value, StringComparison.Ordinal)) { var result = new SecretValidationResult { Success = true, Confirmation = cert.CreateThumbprintCnf() }; return Task.FromResult(result); } } _logger.LogDebug("No matching x509 name secret found."); return fail; } } ================================================ FILE: src/IdentityServer8/src/Validation/Default/X509ThumbprintSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validator for an X.509 certificate based client secret using the thumbprint /// public class X509ThumbprintSecretValidator : ISecretValidator { private readonly ILogger _logger; /// /// ctor /// /// public X509ThumbprintSecretValidator(ILogger logger) { _logger = logger; } /// public Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret) { var fail = Task.FromResult(new SecretValidationResult { Success = false }); if (parsedSecret.Type != ParsedSecretTypes.X509Certificate) { _logger.LogDebug("X509 thumbprint secret validator cannot process {type}", parsedSecret.Type ?? "null"); return fail; } if (!(parsedSecret.Credential is X509Certificate2 cert)) { throw new InvalidOperationException("Credential is not a x509 certificate."); } var thumbprint = cert.Thumbprint; if (thumbprint == null) { _logger.LogWarning("No thumbprint found in X509 certificate."); return fail; } var thumbprintSecrets = secrets.Where(s => s.Type == SecretTypes.X509CertificateThumbprint); if (!thumbprintSecrets.Any()) { _logger.LogDebug("No thumbprint secrets configured for client."); return fail; } foreach (var thumbprintSecret in thumbprintSecrets) { if (thumbprint.Equals(thumbprintSecret.Value, StringComparison.OrdinalIgnoreCase)) { var result = new SecretValidationResult { Success = true, Confirmation = cert.CreateThumbprintCnf() }; return Task.FromResult(result); } } _logger.LogDebug("No matching x509 thumbprint secret found."); return fail; } } ================================================ FILE: src/IdentityServer8/src/Validation/IApiSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validator for handling API client authentication. /// public interface IApiSecretValidator { /// /// Tries to authenticate an API client based on the incoming request /// /// The context. /// Task ValidateAsync(HttpContext context); } ================================================ FILE: src/IdentityServer8/src/Validation/IAuthorizeRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Authorize endpoint request validator. /// public interface IAuthorizeRequestValidator { /// /// Validates authorize request parameters. /// /// /// /// Task ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject = null); } ================================================ FILE: src/IdentityServer8/src/Validation/IClientConfigurationValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validator for handling client authentication /// public interface IClientConfigurationValidator { /// /// Determines whether the configuration of a client is valid. /// /// The context. /// Task ValidateAsync(ClientConfigurationValidationContext context); } ================================================ FILE: src/IdentityServer8/src/Validation/IClientSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validator for handling client authentication /// public interface IClientSecretValidator { /// /// Tries to authenticate a client based on the incoming request /// /// The context. /// Task ValidateAsync(HttpContext context); } ================================================ FILE: src/IdentityServer8/src/Validation/ICustomAuthorizeRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Allows inserting custom validation logic into authorize and token requests /// public interface ICustomAuthorizeRequestValidator { /// /// Custom validation logic for the authorize request. /// /// The context. Task ValidateAsync(CustomAuthorizeRequestValidationContext context); } ================================================ FILE: src/IdentityServer8/src/Validation/ICustomTokenRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Allows inserting custom validation logic into token requests /// public interface ICustomTokenRequestValidator { /// /// Custom validation logic for a token request. /// /// The context. /// /// The validation result /// Task ValidateAsync(CustomTokenRequestValidationContext context); } ================================================ FILE: src/IdentityServer8/src/Validation/ICustomTokenValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Allows inserting custom token validation logic /// public interface ICustomTokenValidator { /// /// Custom validation logic for access tokens. /// /// The validation result so far. /// The validation result Task ValidateAccessTokenAsync(TokenValidationResult result); /// /// Custom validation logic for identity tokens. /// /// The validation result so far. /// The validation result Task ValidateIdentityTokenAsync(TokenValidationResult result); } ================================================ FILE: src/IdentityServer8/src/Validation/IDeviceAuthorizationRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Device authorization endpoint request validator. /// public interface IDeviceAuthorizationRequestValidator { /// /// Validates authorize request parameters. /// /// /// /// Task ValidateAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult); } ================================================ FILE: src/IdentityServer8/src/Validation/IDeviceCodeValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// The device code validator /// public interface IDeviceCodeValidator { /// /// Validates the device code. /// /// The context. /// Task ValidateAsync(DeviceCodeValidationContext context); } ================================================ FILE: src/IdentityServer8/src/Validation/IEndSessionRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates end session requests. /// public interface IEndSessionRequestValidator { /// /// Validates end session endpoint requests. /// /// /// /// Task ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject); /// /// Validates requests from logout page iframe to trigger single signout. /// /// /// Task ValidateCallbackAsync(NameValueCollection parameters); } ================================================ FILE: src/IdentityServer8/src/Validation/IExtensionGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Handles validation of token requests using custom grant types /// public interface IExtensionGrantValidator { /// /// Validates the custom grant request. /// /// The context. /// /// A principal /// Task ValidateAsync(ExtensionGrantValidationContext context); /// /// Returns the grant type this validator can deal with /// /// /// The type of the grant. /// string GrantType { get; } } ================================================ FILE: src/IdentityServer8/src/Validation/IIntrospectionRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Interface for the introspection request validator /// public interface IIntrospectionRequestValidator { /// /// Validates the request. /// /// The parameters. /// The API. /// Task ValidateAsync(NameValueCollection parameters, ApiResource api); } ================================================ FILE: src/IdentityServer8/src/Validation/IRedirectUriValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models the logic when validating redirect and post logout redirect URIs. /// public interface IRedirectUriValidator { /// /// Determines whether a redirect URI is valid for a client. /// /// The requested URI. /// The client. /// true is the URI is valid; false otherwise. Task IsRedirectUriValidAsync(string requestedUri, Client client); /// /// Determines whether a post logout URI is valid for a client. /// /// The requested URI. /// The client. /// true is the URI is valid; false otherwise. Task IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client); } ================================================ FILE: src/IdentityServer8/src/Validation/IResourceOwnerPasswordValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Handles validation of resource owner password credentials /// public interface IResourceOwnerPasswordValidator { /// /// Validates the resource owner password credential /// /// The context. Task ValidateAsync(ResourceOwnerPasswordValidationContext context); } ================================================ FILE: src/IdentityServer8/src/Validation/IResourceValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validates requested resources (scopes and resource indicators) /// public interface IResourceValidator { // todo: should this be used anywhere we re-create tokens? do we need to re-run scope validation? /// /// Validates the requested resources for the client. /// Task ValidateRequestedResourcesAsync(ResourceValidationRequest request); } ================================================ FILE: src/IdentityServer8/src/Validation/IScopeParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Allows parsing raw scopes values into structured scope values. /// public interface IScopeParser { // todo: test return no error, and no parsed scopes. how do callers behave? /// /// Parses the requested scopes. /// ParsedScopesResult ParseScopeValues(IEnumerable scopeValues); } ================================================ FILE: src/IdentityServer8/src/Validation/ISecretParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// A service for parsing secrets found on the request /// public interface ISecretParser { /// /// Tries to find a secret on the context that can be used for authentication /// /// The HTTP context. /// /// A parsed secret /// Task ParseAsync(HttpContext context); /// /// Returns the authentication method name that this parser implements /// /// /// The authentication method. /// string AuthenticationMethod { get; } } ================================================ FILE: src/IdentityServer8/src/Validation/ISecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Service for validating a received secret against a stored secret /// public interface ISecretValidator { /// /// Validates a secret /// /// The stored secrets. /// The received secret. /// A validation result Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret); } ================================================ FILE: src/IdentityServer8/src/Validation/ISecretsListParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Parser for finding the best secret in an Enumerable List /// public interface ISecretsListParser { /// /// Tries to find the best secret on the context that can be used for authentication /// /// The HTTP context. /// /// A parsed secret /// Task ParseAsync(HttpContext context); /// /// Gets all available authentication methods. /// /// IEnumerable GetAvailableAuthenticationMethods(); } ================================================ FILE: src/IdentityServer8/src/Validation/ISecretsListValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validator for an Enumerable List of Secrets /// public interface ISecretsListValidator { /// /// Validates a list of secrets /// /// The stored secrets. /// The received secret. /// A validation result Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret); } ================================================ FILE: src/IdentityServer8/src/Validation/ITokenRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Interface for the token request validator /// public interface ITokenRequestValidator { /// /// Validates the request. /// /// The parameters. /// The client validation result. /// Task ValidateRequestAsync(NameValueCollection parameters, ClientSecretValidationResult clientValidationResult); } ================================================ FILE: src/IdentityServer8/src/Validation/ITokenRevocationRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Interface for the token revocation request validator /// public interface ITokenRevocationRequestValidator { /// /// Validates the request. /// /// The parameters. /// The client. /// Task ValidateRequestAsync(NameValueCollection parameters, Client client); } ================================================ FILE: src/IdentityServer8/src/Validation/ITokenValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Interface for the token validator /// public interface ITokenValidator { /// /// Validates an access token. /// /// The access token. /// The expected scope. /// Task ValidateAccessTokenAsync(string token, string expectedScope = null); /// /// Validates an identity token. /// /// The token. /// The client identifier. /// if set to true the lifetime gets validated. Otherwise not. /// Task ValidateIdentityTokenAsync(string token, string clientId = null, bool validateLifetime = true); } ================================================ FILE: src/IdentityServer8/src/Validation/IUserInfoRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validator for userinfo requests /// public interface IUserInfoRequestValidator { /// /// Validates a userinfo request. /// /// The access token. /// Task ValidateRequestAsync(string accessToken); } ================================================ FILE: src/IdentityServer8/src/Validation/Models/AuthorizeRequestValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for authorize requests /// public class AuthorizeRequestValidationResult : ValidationResult { /// /// Initializes a new instance of the class. /// /// The request. public AuthorizeRequestValidationResult(ValidatedAuthorizeRequest request) { ValidatedRequest = request; IsError = false; } /// /// Initializes a new instance of the class. /// /// The request. /// The error. /// The error description. public AuthorizeRequestValidationResult(ValidatedAuthorizeRequest request, string error, string errorDescription = null) { ValidatedRequest = request; IsError = true; Error = error; ErrorDescription = errorDescription; } /// /// Gets or sets the validated request. /// /// /// The validated request. /// public ValidatedAuthorizeRequest ValidatedRequest { get; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/BearerTokenUsageType.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Validation; public enum BearerTokenUsageType { AuthorizationHeader = 0, PostBody = 1, QueryString = 2 } ================================================ FILE: src/IdentityServer8/src/Validation/Models/BearerTokenUsageValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models usage of a bearer token /// public class BearerTokenUsageValidationResult { /// /// Gets or sets a value indicating whether the token was found. /// /// /// true if the token was found; otherwise, false. /// public bool TokenFound { get; set; } /// /// Gets or sets the token. /// /// /// The token. /// public string Token { get; set; } /// /// Gets or sets the usage type. /// /// /// The type of the usage. /// public BearerTokenUsageType UsageType { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ClientSecretValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for client validation /// public class ClientSecretValidationResult : ValidationResult { /// /// Gets or sets the client. /// /// /// The client. /// public Client Client { get; set; } /// /// Gets or sets the secret used to authenticate the client. /// /// /// The secret. /// public ParsedSecret Secret { get; set; } /// /// Gets or sets the value of the confirmation method (will become the cnf claim). Must be a JSON object. /// /// /// The confirmation. /// public string Confirmation { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/DeviceAuthorizationRequestValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for device authorization requests /// public class DeviceAuthorizationRequestValidationResult : ValidationResult { /// /// Initializes a new instance of the class. /// /// The request. public DeviceAuthorizationRequestValidationResult(ValidatedDeviceAuthorizationRequest request) { IsError = false; ValidatedRequest = request; } /// /// Initializes a new instance of the class. /// /// The request. /// The error. /// The error description. public DeviceAuthorizationRequestValidationResult(ValidatedDeviceAuthorizationRequest request, string error, string errorDescription = null) { IsError = true; Error = error; ErrorDescription = errorDescription; ValidatedRequest = request; } /// /// Gets the validated request. /// /// /// The validated request. /// public ValidatedDeviceAuthorizationRequest ValidatedRequest { get; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/DeviceCodeValidationContext.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for device code validation request. /// public class DeviceCodeValidationContext { /// /// Gets or sets the device code. /// /// /// The device code. /// public string DeviceCode { get; set; } /// /// Gets or sets the request. /// /// /// The request. /// public ValidatedTokenRequest Request { get; set; } /// /// Gets or sets the result. /// /// /// The result. /// public TokenRequestValidationResult Result { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/EndSessionCallbackValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for end session callback requests. /// /// public class EndSessionCallbackValidationResult : ValidationResult { /// /// Gets the client front-channel logout urls. /// public IEnumerable FrontChannelLogoutUrls { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/EndSessionValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for end session requests /// /// public class EndSessionValidationResult : ValidationResult { /// /// Gets or sets the validated request. /// /// /// The validated request. /// public ValidatedEndSessionRequest ValidatedRequest { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/GrantValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models the result of custom grant validation. /// public class GrantValidationResult : ValidationResult { /// /// Gets or sets the principal which represents the result of the validation. /// /// /// The principal. /// public ClaimsPrincipal Subject { get; set; } /// /// Custom fields for the token response /// public Dictionary CustomResponse { get; set; } = new Dictionary(); /// /// Initializes a new instance of the class with no subject. /// Warning: the resulting access token will only contain the client identity. /// public GrantValidationResult(Dictionary customResponse = null) { IsError = false; CustomResponse = customResponse; } /// /// Initializes a new instance of the class with a given principal. /// Warning: the principal needs to include the required claims - it is recommended to use the other constructor that does validation. /// public GrantValidationResult(ClaimsPrincipal principal, Dictionary customResponse = null) { IsError = false; if (principal.Identities.Count() != 1) throw new InvalidOperationException("only a single identity supported"); if (principal.FindFirst(JwtClaimTypes.Subject) == null) throw new InvalidOperationException("sub claim is missing"); if (principal.FindFirst(JwtClaimTypes.IdentityProvider) == null) throw new InvalidOperationException("idp claim is missing"); if (principal.FindFirst(JwtClaimTypes.AuthenticationMethod) == null) throw new InvalidOperationException("amr claim is missing"); if (principal.FindFirst(JwtClaimTypes.AuthenticationTime) == null) throw new InvalidOperationException("auth_time claim is missing"); Subject = principal; CustomResponse = customResponse; } /// /// Initializes a new instance of the class with an error and description. /// /// The error. /// The error description. /// Custom response elements public GrantValidationResult(TokenRequestErrors error, string errorDescription = null, Dictionary customResponse = null) { Error = ConvertTokenErrorEnumToString(error); ErrorDescription = errorDescription; CustomResponse = customResponse; } /// /// Initializes a new instance of the class. /// /// The subject claim used to uniquely identifier the user. /// The authentication method which describes the custom grant type. /// Additional claims that will be maintained in the principal. /// If you want these claims to appear in token, you need to add them explicitly in your custom implementation of service. /// The identity provider. /// The custom response. public GrantValidationResult( string subject, string authenticationMethod, IEnumerable claims = null, string identityProvider = IdentityServerConstants.LocalIdentityProvider, Dictionary customResponse = null) : this(subject, authenticationMethod, DateTime.UtcNow, claims, identityProvider, customResponse) { } /// /// Initializes a new instance of the class. /// /// The subject claim used to uniquely identifier the user. /// The authentication method which describes the custom grant type. /// The UTC date/time of authentication. /// Additional claims that will be maintained in the principal. /// If you want these claims to appear in token, you need to add them explicitly in your custom implementation of service. /// The identity provider. /// The custom response. public GrantValidationResult( string subject, string authenticationMethod, DateTime authTime, IEnumerable claims = null, string identityProvider = IdentityServerConstants.LocalIdentityProvider, Dictionary customResponse = null) { IsError = false; var resultClaims = new List { new Claim(JwtClaimTypes.Subject, subject), new Claim(JwtClaimTypes.AuthenticationMethod, authenticationMethod), new Claim(JwtClaimTypes.IdentityProvider, identityProvider), new Claim(JwtClaimTypes.AuthenticationTime, new DateTimeOffset(authTime).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) }; if (!claims.EnumerableIsNullOrEmpty()) { resultClaims.AddRange(claims); } var id = new ClaimsIdentity(authenticationMethod); id.AddClaims(resultClaims.Distinct(new ClaimComparer())); Subject = new ClaimsPrincipal(id); CustomResponse = customResponse; } private string ConvertTokenErrorEnumToString(TokenRequestErrors error) { return error switch { TokenRequestErrors.InvalidClient => OidcConstants.TokenErrors.InvalidClient, TokenRequestErrors.InvalidGrant => OidcConstants.TokenErrors.InvalidGrant, TokenRequestErrors.InvalidRequest => OidcConstants.TokenErrors.InvalidRequest, TokenRequestErrors.InvalidScope => OidcConstants.TokenErrors.InvalidScope, TokenRequestErrors.UnauthorizedClient => OidcConstants.TokenErrors.UnauthorizedClient, TokenRequestErrors.UnsupportedGrantType => OidcConstants.TokenErrors.UnsupportedGrantType, TokenRequestErrors.InvalidTarget => OidcConstants.TokenErrors.InvalidTarget, _ => throw new InvalidOperationException("invalid token error") }; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/IntrospectionRequestValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for introspection request /// /// public class IntrospectionRequestValidationResult : ValidationResult { /// /// Gets or sets the request parameters. /// /// /// The parameters. /// public NameValueCollection Parameters { get; set; } /// /// Gets or sets the API. /// /// /// The API. /// public ApiResource Api { get; set; } /// /// Gets or sets a value indicating whether the token is active. /// /// /// true if the token is active; otherwise, false. /// public bool IsActive { get; set; } /// /// Gets or sets the claims. /// /// /// The claims. /// public IEnumerable Claims { get; set; } /// /// Gets or sets the token. /// /// /// The token. /// public string Token { get; set; } } /// /// Failure reasons for introspection request /// public enum IntrospectionRequestValidationFailureReason { /// /// none /// None, /// /// missing token /// MissingToken, /// /// invalid token /// InvalidToken, /// /// invalid scope /// InvalidScope } ================================================ FILE: src/IdentityServer8/src/Validation/Models/JwtRequestValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models the result of JWT request validation. /// public class JwtRequestValidationResult : ValidationResult { /// /// The key/value pairs from the JWT payload of a successfuly validated request. /// public Dictionary Payload { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ParsedScopeValidationError.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models an error parsing a scope. /// public class ParsedScopeValidationError { /// /// Ctor /// /// /// public ParsedScopeValidationError(string rawValue, string error) { if (String.IsNullOrWhiteSpace(rawValue)) { throw new ArgumentNullException(nameof(rawValue)); } if (String.IsNullOrWhiteSpace(error)) { throw new ArgumentNullException(nameof(error)); } RawValue = rawValue; Error = error; } /// /// The original (raw) value of the scope. /// public string RawValue { get; set; } /// /// Error message describing why the raw scope failed to be parsed. /// public string Error { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ParsedScopeValue.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models a parsed scope value. /// public class ParsedScopeValue { /// /// Ctor /// /// public ParsedScopeValue(string rawValue) : this(rawValue, rawValue, null) { } /// /// Ctor /// /// /// /// public ParsedScopeValue(string rawValue, string parsedName, string parsedParameter) { if (String.IsNullOrWhiteSpace(rawValue)) { throw new ArgumentNullException(nameof(rawValue)); } if (String.IsNullOrWhiteSpace(parsedName)) { throw new ArgumentNullException(nameof(parsedName)); } RawValue = rawValue; ParsedName = parsedName; ParsedParameter = parsedParameter; } /// /// The original (raw) value of the scope. /// public string RawValue { get; set; } /// /// The parsed name of the scope. If the scope has no structure, the parsed name will be the same as the raw value. /// public string ParsedName { get; set; } // future: maybe this should be something w/ more structure? dictionary? /// /// The parameter value of the parsed scope. If the scope has no structure, then the value will be null. /// public string ParsedParameter { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ParsedScopesResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Represents the result of scope parsing. /// public class ParsedScopesResult { /// /// The valid parsed scopes. /// public ICollection ParsedScopes { get; set; } = new HashSet(); /// /// The errors encountered while parsing. /// public ICollection Errors { get; set; } = new HashSet(); /// /// Indicates if the result of parsing the scopes was successful. /// public bool Succeeded => Errors == null || !Errors.Any(); } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ResourceValidationRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models the request to validate scopes and resource indicators for a client. /// public class ResourceValidationRequest { /// /// The client. /// public Client Client { get; set; } /// /// The requested scope values. /// public IEnumerable Scopes { get; set; } // /// // /// The requested resource indicators. // /// // todo: add back when we support resource indicators // public IEnumerable ResourceIndicators { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ResourceValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Result of validation of requested scopes and resource indicators. /// public class ResourceValidationResult { /// /// Ctor /// public ResourceValidationResult() { } /// /// Ctor /// /// public ResourceValidationResult(Resources resources) { Resources = resources; ParsedScopes = resources.ToScopeNames().Select(x => new ParsedScopeValue(x)).ToList(); } /// /// Ctor /// /// /// public ResourceValidationResult(Resources resources, IEnumerable parsedScopeValues) { Resources = resources; ParsedScopes = parsedScopeValues.ToList(); } /// /// Indicates if the result was successful. /// public bool Succeeded => ParsedScopes.Any() && !InvalidScopes.Any(); /// /// The resources of the result. /// public Resources Resources { get; set; } = new Resources(); /// /// The parsed scopes represented by the result. /// public ICollection ParsedScopes { get; set; } = new HashSet(); /// /// The original (raw) scope values represented by the validated result. /// public IEnumerable RawScopeValues => ParsedScopes.Select(x => x.RawValue); /// /// The requested scopes that are invalid. /// public ICollection InvalidScopes { get; set; } = new HashSet(); /// /// Returns new result filted by the scope values. /// /// /// public ResourceValidationResult Filter(IEnumerable scopeValues) { scopeValues ??= Enumerable.Empty(); var offline = scopeValues.Contains(IdentityServerConstants.StandardScopes.OfflineAccess); var parsedScopesToKeep = ParsedScopes.Where(x => scopeValues.Contains(x.RawValue)).ToArray(); var parsedScopeNamesToKeep = parsedScopesToKeep.Select(x => x.ParsedName).ToArray(); var identityToKeep = Resources.IdentityResources.Where(x => parsedScopeNamesToKeep.Contains(x.Name)); var apiScopesToKeep = Resources.ApiScopes.Where(x => parsedScopeNamesToKeep.Contains(x.Name)); var apiScopesNamesToKeep = apiScopesToKeep.Select(x => x.Name).ToArray(); var apiResourcesToKeep = Resources.ApiResources.Where(x => x.Scopes.Any(y => apiScopesNamesToKeep.Contains(y))); var resources = new Resources(identityToKeep, apiResourcesToKeep, apiScopesToKeep) { OfflineAccess = offline }; return new ResourceValidationResult() { Resources = resources, ParsedScopes = parsedScopesToKeep }; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ScopeSecretValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for API validation /// public class ApiSecretValidationResult : ValidationResult { /// /// Gets or sets the resource. /// /// /// The resource. /// public ApiResource Resource { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/SecretValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for secrets /// public class SecretValidationResult : ValidationResult { /// /// Gets or sets a value indicating whether the secret validation was successful. /// /// /// true if success; otherwise, false. /// public bool Success { get; set; } /// /// Gets or sets the value of the confirmation method (will become the cnf claim). Must be a JSON object. /// /// /// The confirmation. /// public string Confirmation { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/TokenRequestValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for token requests /// public class TokenRequestValidationResult : ValidationResult { /// /// Initializes a new instance of the class. /// /// The validated request. /// The custom response. public TokenRequestValidationResult(ValidatedTokenRequest validatedRequest, Dictionary customResponse = null) { IsError = false; ValidatedRequest = validatedRequest; CustomResponse = customResponse; } /// /// Initializes a new instance of the class. /// /// The validated request. /// The error. /// The error description. /// The custom response. public TokenRequestValidationResult(ValidatedTokenRequest validatedRequest, string error, string errorDescription = null, Dictionary customResponse = null) { IsError = true; Error = error; ErrorDescription = errorDescription; ValidatedRequest = validatedRequest; CustomResponse = customResponse; } /// /// Gets the validated request. /// /// /// The validated request. /// public ValidatedTokenRequest ValidatedRequest { get; } /// /// Gets or sets the custom response. /// /// /// The custom response. /// public Dictionary CustomResponse { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/TokenRevocationRequestValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models the validation result of access tokens and id tokens. /// public class TokenRevocationRequestValidationResult : ValidationResult { /// /// Gets or sets the token type hint. /// /// /// The token type hint. /// public string TokenTypeHint { get; set; } /// /// Gets or sets the token. /// /// /// The token. /// public string Token { get; set; } /// /// Gets or sets the client. /// /// /// The client. /// public Client Client { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/TokenValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models the validation result of access tokens and id tokens. /// public class TokenValidationResult : ValidationResult { /// /// Gets or sets the claims. /// /// /// The claims. /// public IEnumerable Claims { get; set; } /// /// Gets or sets the JWT. /// /// /// The JWT. /// public string Jwt { get; set; } /// /// Gets or sets the reference token (in case of access token validation). /// /// /// The reference token. /// public Token ReferenceToken { get; set; } /// /// Gets or sets the reference token identifier (in case of access token validation). /// /// /// The reference token identifier. /// public string ReferenceTokenId { get; set; } /// /// Gets or sets the refresh token (in case of refresh token validation). /// /// /// The reference token identifier. /// public RefreshToken RefreshToken { get; set; } /// /// Gets or sets the client. /// /// /// The client. /// public Client Client { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/UserInfoRequestValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Validation result for userinfo requests /// public class UserInfoRequestValidationResult : ValidationResult { /// /// Gets or sets the token validation result. /// /// /// The token validation result. /// public TokenValidationResult TokenValidationResult { get; set; } /// /// Gets or sets the subject. /// /// /// The subject. /// public ClaimsPrincipal Subject { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ValidatedAuthorizeRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models a validated request to the authorize endpoint. /// public class ValidatedAuthorizeRequest : ValidatedRequest { /// /// Gets or sets the type of the response. /// /// /// The type of the response. /// public string ResponseType { get; set; } /// /// Gets or sets the response mode. /// /// /// The response mode. /// public string ResponseMode { get; set; } /// /// Gets or sets the grant type. /// /// /// The grant type. /// public string GrantType { get; set; } /// /// Gets or sets the redirect URI. /// /// /// The redirect URI. /// public string RedirectUri { get; set; } /// /// Gets or sets the requested scopes. /// /// /// The requested scopes. /// // todo: consider replacing with extension method to access Raw collection; would neeed to be done wholesale for all props. public List RequestedScopes { get; set; } /// /// Gets or sets a value indicating whether consent was shown. /// /// /// true if consent was shown; otherwise, false. /// public bool WasConsentShown { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } /// /// Gets or sets the state. /// /// /// The state. /// public string State { get; set; } /// /// Gets or sets the UI locales. /// /// /// The UI locales. /// public string UiLocales { get; set; } /// /// Gets or sets a value indicating whether the request was an OpenID Connect request. /// /// /// true if the request was an OpenID Connect request; otherwise, false. /// public bool IsOpenIdRequest { get; set; } /// /// Gets or sets a value indicating whether this instance is API resource request. /// /// /// true if this instance is API resource request; otherwise, false. /// public bool IsApiResourceRequest { get; set; } /// /// Gets or sets the nonce. /// /// /// The nonce. /// public string Nonce { get; set; } /// /// Gets or sets the authentication context reference classes. /// /// /// The authentication context reference classes. /// public List AuthenticationContextReferenceClasses { get; set; } /// /// Gets or sets the display mode. /// /// /// The display mode. /// public string DisplayMode { get; set; } /// /// Gets or sets the collection of prompt modes. /// /// /// The collection of prompt modes. /// public IEnumerable PromptModes { get; set; } = Enumerable.Empty(); /// /// Gets or sets the maximum age. /// /// /// The maximum age. /// public int? MaxAge { get; set; } /// /// Gets or sets the login hint. /// /// /// The login hint. /// public string LoginHint { get; set; } /// /// Gets or sets the code challenge /// /// /// The code challenge /// public string CodeChallenge { get; set; } /// /// Gets or sets the code challenge method /// /// /// The code challenge method /// public string CodeChallengeMethod { get; set; } /// /// Gets or sets the validated contents of the request object (if present) /// /// /// The request object values /// public Dictionary RequestObjectValues { get; set; } = new Dictionary(); /// /// Gets or sets the request object (either passed by value or retrieved by reference) /// /// /// The request object /// public string RequestObject { get; set; } /// /// Gets a value indicating whether an access token was requested. /// /// /// true if an access token was requested; otherwise, false. /// public bool AccessTokenRequested => ResponseType == OidcConstants.ResponseTypes.IdTokenToken || ResponseType == OidcConstants.ResponseTypes.Code || ResponseType == OidcConstants.ResponseTypes.CodeIdToken || ResponseType == OidcConstants.ResponseTypes.CodeToken || ResponseType == OidcConstants.ResponseTypes.CodeIdTokenToken; /// /// Initializes a new instance of the class. /// public ValidatedAuthorizeRequest() { RequestedScopes = new List(); AuthenticationContextReferenceClasses = new List(); } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ValidatedDeviceAuthorizationRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models a validated request to the device authorization endpoint. /// public class ValidatedDeviceAuthorizationRequest : ValidatedRequest { /// /// Gets or sets the requested scopes. /// /// /// The scopes. /// public IEnumerable RequestedScopes { get; set; } /// /// Gets or sets a value indicating whether this instance is open identifier request. /// /// /// true if this instance is open identifier request; otherwise, false. /// public bool IsOpenIdRequest { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ValidatedEndSessionRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Represents a validated end session (logout) request /// public class ValidatedEndSessionRequest : ValidatedRequest { /// /// Gets a value indicating whether this instance is authenticated. /// /// /// true if this instance is authenticated; otherwise, false. /// public bool IsAuthenticated => Client != null; /// /// Gets or sets the post-logout URI. /// /// /// The post-logout URI. /// public string PostLogOutUri { get; set; } /// /// Gets or sets the state. /// /// /// The state. /// public string State { get; set; } /// /// Ids of clients known to have an authentication session for user at end session time /// public IEnumerable ClientIds { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ValidatedRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Base class for a validate authorize or token request /// public class ValidatedRequest { /// /// Gets or sets the raw request data /// /// /// The raw. /// public NameValueCollection Raw { get; set; } /// /// Gets or sets the client. /// /// /// The client. /// public Client Client { get; set; } /// /// Gets or sets the secret used to authenticate the client. /// /// /// The parsed secret. /// public ParsedSecret Secret { get; set; } /// /// Gets or sets the effective access token lifetime for the current request. /// This value is initally read from the client configuration but can be modified in the request pipeline /// public int AccessTokenLifetime { get; set; } /// /// Gets or sets the client claims for the current request. /// This value is initally read from the client configuration but can be modified in the request pipeline /// public ICollection ClientClaims { get; set; } = new HashSet(new ClaimComparer()); /// /// Gets or sets the effective access token type for the current request. /// This value is initally read from the client configuration but can be modified in the request pipeline /// public AccessTokenType AccessTokenType { get; set; } /// /// Gets or sets the subject. /// /// /// The subject. /// public ClaimsPrincipal Subject { get; set; } /// /// Gets or sets the session identifier. /// /// /// The session identifier. /// public string SessionId { get; set; } /// /// Gets or sets the identity server options. /// /// /// The options. /// public IdentityServerOptions Options { get; set; } /// /// Gets or sets the validated resources for the request. /// /// /// The validated resources. /// public ResourceValidationResult ValidatedResources { get; set; } = new ResourceValidationResult(); /// /// Gets or sets the value of the confirmation method (will become the cnf claim). Must be a JSON object. /// /// /// The confirmation. /// public string Confirmation { get; set; } /// /// Gets or sets the client ID that should be used for the current request (this is useful for token exchange scenarios) /// /// /// The client ID /// public string ClientId { get; set; } /// /// Sets the client and the appropriate request specific settings. /// /// The client. /// The client secret (optional). /// The confirmation. /// client public void SetClient(Client client, ParsedSecret secret = null, string confirmation = "") { Client = client ?? throw new ArgumentNullException(nameof(client)); Secret = secret; Confirmation = confirmation; ClientId = client.ClientId; AccessTokenLifetime = client.AccessTokenLifetime; AccessTokenType = client.AccessTokenType; ClientClaims = client.Claims.Select(c => new Claim(c.Type, c.Value, c.ValueType)).ToList(); } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ValidatedTokenRequest.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Models a validated request to the token endpoint. /// public class ValidatedTokenRequest : ValidatedRequest { /// /// Gets or sets the type of the grant. /// /// /// The type of the grant. /// public string GrantType { get; set; } /// /// Gets or sets the scopes. /// /// /// The scopes. /// public IEnumerable RequestedScopes { get; set; } /// /// Gets or sets the username used in the request. /// /// /// The name of the user. /// public string UserName { get; set; } /// /// Gets or sets the refresh token. /// /// /// The refresh token. /// public RefreshToken RefreshToken { get; set; } /// /// Gets or sets the refresh token handle. /// /// /// The refresh token handle. /// public string RefreshTokenHandle { get; set; } /// /// Gets or sets the authorization code. /// /// /// The authorization code. /// public AuthorizationCode AuthorizationCode { get; set; } /// /// Gets or sets the authorization code handle. /// /// /// The authorization code handle. /// public string AuthorizationCodeHandle { get; set; } /// /// Gets or sets the code verifier. /// /// /// The code verifier. /// public string CodeVerifier { get; set; } /// /// Gets or sets the device code. /// /// /// The device code. /// public DeviceCode DeviceCode { get; set; } } ================================================ FILE: src/IdentityServer8/src/Validation/Models/ValidationResult.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Validation; /// /// Minimal validation result class (base-class for more complext validation results) /// public class ValidationResult { /// /// Gets or sets a value indicating whether the validation was successful. /// /// /// true if the validation is failed; otherwise, false. /// public bool IsError { get; set; } = true; /// /// Gets or sets the error. /// /// /// The error. /// public string Error { get; set; } /// /// Gets or sets the error description. /// /// /// The error description. /// public string ErrorDescription { get; set; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ClientAssertionClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using IdentityServer.IntegrationTests.Common; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Microsoft.IdentityModel.Tokens; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Text; using System.Text.Json; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class ClientAssertionClient { private const string TokenEndpoint = "https://idsvr8/connect/token"; private const string ClientId = "certificate_base64_valid"; private readonly HttpClient _client; public ClientAssertionClient() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Valid_client_with_manual_payload_should_succeed() { var token = CreateToken(ClientId); var requestBody = new FormUrlEncodedContent(new Dictionary { { "client_id", ClientId }, { "client_assertion_type", "urn:ietf:params:oauth:client-assertion-type:jwt-bearer" }, { "client_assertion", token }, { "grant_type", "client_credentials" }, { "scope", "api1" } }); var response = await GetToken(requestBody); AssertValidToken(response); } [Fact] public async Task Valid_client_should_succeed() { var token = CreateToken(ClientId); var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientCredentialStyle = ClientCredentialStyle.PostBody, GrantType = "client_credentials", ClientId = ClientId, ClientAssertion = { Type = OidcConstants.ClientAssertionTypes.JwtBearer, Value = token }, Scope = "api1" }); AssertValidToken(response); } [Fact] public async Task Valid_client_with_implicit_clientId_should_succeed() { var token = CreateToken(ClientId); var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientCredentialStyle = ClientCredentialStyle.PostBody, GrantType = "client_credentials", ClientAssertion = { Type = OidcConstants.ClientAssertionTypes.JwtBearer, Value = token }, Scope = "api1" }); AssertValidToken(response); } [Fact] public async Task Valid_client_with_token_replay_should_fail() { var token = CreateToken(ClientId); var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientCredentialStyle = ClientCredentialStyle.PostBody, GrantType = "client_credentials", ClientId = ClientId, ClientAssertion = { Type = OidcConstants.ClientAssertionTypes.JwtBearer, Value = token }, Scope = "api1" }); AssertValidToken(response); // replay response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientCredentialStyle = ClientCredentialStyle.PostBody, GrantType = "client_credentials", ClientId = ClientId, ClientAssertion = { Type = OidcConstants.ClientAssertionTypes.JwtBearer, Value = token }, Scope = "api1" }); response.IsError.Should().BeTrue(); response.Error.Should().Be("invalid_client"); } [Fact] public async Task Client_with_invalid_secret_should_fail() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, GrantType = "client_credentials", ClientCredentialStyle = ClientCredentialStyle.PostBody, ClientId = ClientId, ClientAssertion = { Type = OidcConstants.ClientAssertionTypes.JwtBearer, Value = "invalid" }, Scope = "api1" }); response.IsError.Should().Be(true); response.Error.Should().Be(OidcConstants.TokenErrors.InvalidClient); response.ErrorType.Should().Be(ResponseErrorType.Protocol); } [Fact] public async Task Invalid_client_should_fail() { const string clientId = "certificate_base64_invalid"; var token = CreateToken(clientId); var tokenRequest = new ClientCredentialsTokenRequest(); tokenRequest.Address = TokenEndpoint; tokenRequest.ClientId = clientId; tokenRequest.GrantType = "client_credentials"; tokenRequest.ClientAssertion.Type = OidcConstants.ClientAssertionTypes.JwtBearer; tokenRequest.ClientAssertion.Value = token; tokenRequest.Scope = "api1"; tokenRequest.ClientCredentialStyle = ClientCredentialStyle.PostBody; var response = await _client.RequestTokenAsync(tokenRequest); response.IsError.Should().Be(true); response.Error.Should().Be(OidcConstants.TokenErrors.InvalidClient); response.ErrorType.Should().Be(ResponseErrorType.Protocol); } private async Task GetToken(FormUrlEncodedContent body) { var response = await _client.PostAsync(TokenEndpoint, body); return await ProtocolResponse.FromHttpResponseAsync(response); } private void AssertValidToken(TokenResponse response) { response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(8); payload.Keys.Should().Contain("iss"); payload.Keys.Should().Contain("client_id"); payload["iss"].ValueKind.Should().Be(JsonValueKind.String); payload["client_id"].ValueKind.Should().Be(JsonValueKind.String); payload["iss"].ToString().Should().Be("https://idsvr8"); payload["client_id"].ToString().Should().Be(ClientId); payload["scope"].ValueKind.Should().Be(JsonValueKind.Array); var scopes = payload["scope"].EnumerateArray(); scopes.First().ToString().Should().Be("api1"); payload["aud"].ToString().Should().Be("api"); } private Dictionary GetPayload(TokenResponse response) { var token = response.AccessToken.Split('.').Skip(1).Take(1).First(); var dictionary = JsonSerializer.Deserialize>( Encoding.UTF8.GetString(Base64Url.Decode(token))); return dictionary; } private string CreateToken(string clientId, DateTime? nowOverride = null) { var certificate = TestCert.Load(); var now = nowOverride ?? DateTime.UtcNow; var token = new JwtSecurityToken( clientId, TokenEndpoint, new List() { new Claim("jti", Guid.NewGuid().ToString()), new Claim(JwtClaimTypes.Subject, clientId), new Claim(JwtClaimTypes.IssuedAt, new DateTimeOffset(now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) }, now, now.AddMinutes(1), new SigningCredentials( new X509SecurityKey(certificate), SecurityAlgorithms.RsaSha256 ) ); var tokenHandler = new JwtSecurityTokenHandler(); return tokenHandler.WriteToken(token); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ClientCredentialsAndResourceOwnerClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class ClientCredentialsandResourceOwnerClient { private const string TokenEndpoint = "https://server/connect/token"; private readonly HttpClient _client; public ClientCredentialsandResourceOwnerClient() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Resource_scope_should_be_requestable_via_client_credentials() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client.and.ro", ClientSecret = "secret", Scope = "api1" }); response.IsError.Should().Be(false); } [Fact] public async Task Openid_scope_should_not_be_requestable_via_client_credentials() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client.and.ro", ClientSecret = "secret", Scope = "openid api1" }); response.IsError.Should().Be(true); } [Fact] public async Task Openid_scope_should_be_requestable_via_password() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "client.and.ro", ClientSecret = "secret", Scope = "openid", UserName = "bob", Password = "bob" }); response.IsError.Should().Be(false); } [Fact] public async Task Openid_and_resource_scope_should_be_requestable_via_password() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "client.and.ro", ClientSecret = "secret", Scope = "openid api1", UserName = "bob", Password = "bob" }); response.IsError.Should().Be(false); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ClientCredentialsClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using System.Net; using System.Text; using System.Text.Json; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class ClientCredentialsClient { private const string TokenEndpoint = "https://server/connect/token"; private readonly HttpClient _client; public ClientCredentialsClient() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Invalid_endpoint_should_return_404() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint + "invalid", ClientId = "client", ClientSecret = "secret", Scope = "api1" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Http); response.Error.Should().Be("Not Found"); response.HttpStatusCode.Should().Be(HttpStatusCode.NotFound); } [Fact] public async Task Valid_request_single_audience_should_return_expected_payload() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(8); payload["iss"].ToString().Should().Be("https://idsvr8"); payload["client_id"].ToString().Should().Be("client"); payload["aud"].ToString().Should().Be("api"); payload.Keys.Should().Contain("jti"); payload.Keys.Should().Contain("iat"); payload["aud"].ToString().Should().Be("api"); var scopes = payload["scope"].EnumerateArray().Select(x => x.GetString()); scopes.First().ToString().Should().Be("api1"); } [Fact] public async Task Valid_request_multiple_audiences_should_return_expected_payload() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1 other_api" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(8); payload["iss"].ToString().Should().Be("https://idsvr8"); payload["client_id"].ToString().Should().Be("client"); payload.Keys.Should().Contain("jti"); payload.Keys.Should().Contain("iat"); var audiences = payload["aud"].EnumerateArray().Select(x => x.GetString()); audiences.Count().Should().Be(2); audiences.Should().Contain("api"); audiences.Should().Contain("other_api"); var scopes = payload["scope"].EnumerateArray().Select(x => x.ToString()); scopes.First().ToString().Should().Be("api1"); } [Fact] public async Task Valid_request_with_confirmation_should_return_expected_payload() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client.cnf", ClientSecret = "foo", Scope = "api1" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(9); payload["iss"].ToString().Should().Be("https://idsvr8"); payload["client_id"].ToString().Should().Be("client.cnf"); payload["aud"].ToString().Should().Be("api"); payload.Keys.Should().Contain("jti"); payload.Keys.Should().Contain("iat"); var scopes = payload["scope"].EnumerateArray().Select(x => x.GetString()); scopes.First().ToString().Should().Be("api1"); payload["cnf"].ValueKind.Should().Be(JsonValueKind.Array); var cnfArray = payload["cnf"].EnumerateArray().ToList(); cnfArray.Count.Should().Be(1); var elArray= cnfArray.First().EnumerateArray().ToList(); elArray.Count.Should().Be(1); elArray.First().GetString().Should().Be("foo"); } [Fact] public async Task Requesting_multiple_scopes_should_return_expected_payload() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1 api2" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(8); payload["iss"].ToString().Should().Be("https://idsvr8"); payload["client_id"].ToString().Should().Be("client"); payload["aud"].ToString().Should().Be("api"); payload.Keys.Should().Contain("jti"); payload.Keys.Should().Contain("iat"); var scopes = payload["scope"].EnumerateArray().Select(x => x.GetString()); scopes.Count().Should().Be(2); scopes.First().ToString().Should().Be("api1"); scopes.Skip(1).First().ToString().Should().Be("api2"); } [Fact] public async Task Request_with_no_explicit_scopes_should_return_expected_payload() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(8); payload["iss"].ToString().Should().Be("https://idsvr8"); payload["client_id"].ToString().Should().Be("client"); var scopes = payload["scope"].EnumerateArray().Select(x => x.GetString()); payload.Keys.Should().Contain("jti"); payload.Keys.Should().Contain("iat"); var audiences = payload["aud"].EnumerateArray().Select(x=> x.GetString()); audiences.Count().Should().Be(2); audiences.Should().Contain("api"); audiences.Should().Contain("other_api"); scopes.Count().Should().Be(3); scopes.Should().Contain("api1"); scopes.Should().Contain("api2"); scopes.Should().Contain("other_api"); } [Fact] public async Task Client_without_default_scopes_skipping_scope_parameter_should_return_error() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client.no_default_scopes", ClientSecret = "secret" }); response.IsError.Should().Be(true); response.ExpiresIn.Should().Be(0); response.TokenType.Should().BeNull(); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); response.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] public async Task Request_posting_client_secret_in_body_should_succeed() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1", ClientCredentialStyle = ClientCredentialStyle.PostBody }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload["iss"].ToString().Should().Be("https://idsvr8"); payload["client_id"].ToString().Should().Be("client"); payload["aud"].ToString().Should().Be("api"); var scopes = payload["scope"].EnumerateArray(); scopes.First().ToString().Should().Be("api1"); } [Fact] public async Task Request_For_client_with_no_secret_and_basic_authentication_should_succeed() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client.no_secret", Scope = "api1" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload["iss"].ToString().Should().Be("https://idsvr8"); payload["client_id"].ToString().Should().Be("client.no_secret"); var scopes = payload["scope"].EnumerateArray(); scopes.First().ToString().Should().Be("api1"); } [Fact] public async Task Request_with_invalid_client_secret_should_fail() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "invalid", Scope = "api1" }); response.IsError.Should().Be(true); response.Error.Should().Be("invalid_client"); } [Fact] public async Task Unknown_client_should_fail() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "invalid", ClientSecret = "secret", Scope = "api1" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_client"); } [Fact] public async Task Implicit_client_should_not_use_client_credential_grant() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "implicit", Scope = "api1" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("unauthorized_client"); } [Fact] public async Task Implicit_and_client_creds_client_should_not_use_client_credential_grant_without_secret() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "implicit_and_client_creds", ClientSecret = "invalid", Scope = "api1" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_client"); } [Fact] public async Task Requesting_unknown_scope_should_fail() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "unknown" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_scope"); } [Fact] public async Task Client_explicitly_requesting_identity_scope_should_fail() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client.identityscopes", ClientSecret = "secret", Scope = "openid api1" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_scope"); } [Fact] public async Task Client_explicitly_requesting_offline_access_should_fail() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1 offline_access" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_scope"); } [Fact] public async Task Requesting_unauthorized_scope_should_fail() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api3" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_scope"); } [Fact] public async Task Requesting_authorized_and_unauthorized_scopes_should_fail() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1 api3" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_scope"); } private Dictionary GetPayload(TokenResponse response) { var token = response.AccessToken.Split('.').Skip(1).Take(1).First(); var dictionary = JsonSerializer.Deserialize>( Encoding.UTF8.GetString(Base64Url.Decode(token))); return dictionary; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenRequestValidatorClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using IdentityServer8.Extensions; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using System.Text.Json; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class CustomTokenRequestValidatorClient { private const string TokenEndpoint = "https://server/connect/token"; private readonly HttpClient _client; public CustomTokenRequestValidatorClient() { var val = new TestCustomTokenRequestValidator(); Startup.CustomTokenRequestValidator = val; var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Client_credentials_request_should_contain_custom_response() { var response = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client", ClientSecret = "secret", Scope = "api1" }); ValidateCustomFields(response); } [Fact] public async Task Resource_owner_credentials_request_should_contain_custom_response() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "api1", UserName = "bob", Password = "bob" }); ValidateCustomFields(response); } [Fact] public async Task Refreshing_a_token_should_contain_custom_response() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "api1 offline_access", UserName = "bob", Password = "bob" }); response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", RefreshToken = response.RefreshToken }); ValidateCustomFields(response); } [Fact] public async Task Extension_grant_request_should_contain_custom_response() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "custom_credential", "custom credential"} } }); ValidateCustomFields(response); } private Dictionary GetFields(JsonElement json) { return json.ToObject>(); } private void ValidateCustomFields(TokenResponse response) { var fields = GetFields(response.Json); fields["custom"].ToString().Should().Be("custom"); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/CustomTokenResponseClients.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using IdentityServer8.Extensions; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Text; using System.Text.Json; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class CustomTokenResponseClients { private const string TokenEndpoint = "https://server/connect/token"; private readonly HttpClient _client; public CustomTokenResponseClients() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Resource_owner_success_should_return_custom_response() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", UserName = "bob", Password = "bob", Scope = "api1" }); // raw fields var fields = GetFields(response); fields["string_value"].GetString().Should().Be("some_string"); fields["int_value"].GetInt64().Should().Be(42); JsonElement temp; fields.TryGetValue("identity_token", out temp).Should().BeFalse(); fields.TryGetValue("refresh_token", out temp).Should().BeFalse(); fields.TryGetValue("error", out temp).Should().BeFalse(); fields.TryGetValue("error_description", out temp).Should().BeFalse(); fields.TryGetValue("token_type", out temp).Should().BeTrue(); fields.TryGetValue("expires_in", out temp).Should().BeTrue(); var responseObject = fields["dto"]; responseObject.Should().NotBeNull(); var responseDto = GetDto(responseObject); var dto = CustomResponseDto.Create; responseDto.string_value.Should().Be(dto.string_value); responseDto.int_value.Should().Be(dto.int_value); responseDto.nested.string_value.Should().Be(dto.nested.string_value); responseDto.nested.int_value.Should().Be(dto.nested.int_value); // token client response response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); // token content var payload = GetPayload(response); payload.Count().Should().Be(12); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "roclient"); payload.Should().Contain("sub", "bob"); payload.Should().Contain("idp", "local"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("password"); } [Fact] public async Task Resource_owner_failure_should_return_custom_error_response() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", UserName = "bob", Password = "invalid", Scope = "api1" }); // raw fields var fields = GetFields(response); fields["string_value"].ToString().Should().Be("some_string"); fields["int_value"].GetInt64().Should().Be(42); JsonElement temp; fields.TryGetValue("identity_token", out temp).Should().BeFalse(); fields.TryGetValue("refresh_token", out temp).Should().BeFalse(); fields.TryGetValue("error", out temp).Should().BeTrue(); fields.TryGetValue("error_description", out temp).Should().BeTrue(); fields.TryGetValue("token_type", out temp).Should().BeFalse(); fields.TryGetValue("expires_in", out temp).Should().BeFalse(); var responseObject = fields["dto"]; responseObject.Should().NotBeNull(); var responseDto = GetDto(responseObject); var dto = CustomResponseDto.Create; responseDto.string_value.Should().Be(dto.string_value); responseDto.int_value.Should().Be(dto.int_value); responseDto.nested.string_value.Should().Be(dto.nested.string_value); responseDto.nested.int_value.Should().Be(dto.nested.int_value); // token client response response.IsError.Should().Be(true); response.Error.Should().Be("invalid_grant"); response.ErrorDescription.Should().Be("invalid_credential"); response.ExpiresIn.Should().Be(0); response.TokenType.Should().BeNull(); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); } [Fact] public async Task Extension_grant_success_should_return_custom_response() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "outcome", "succeed"} } }); // raw fields var fields = GetFields(response); fields["string_value"].ToString().Should().Be("some_string"); fields["int_value"].GetInt64().Should().Be(42); JsonElement temp; fields.TryGetValue("identity_token", out temp).Should().BeFalse(); fields.TryGetValue("refresh_token", out temp).Should().BeFalse(); fields.TryGetValue("error", out temp).Should().BeFalse(); fields.TryGetValue("error_description", out temp).Should().BeFalse(); fields.TryGetValue("token_type", out temp).Should().BeTrue(); fields.TryGetValue("expires_in", out temp).Should().BeTrue(); var responseObject = fields["dto"]; responseObject.Should().NotBeNull(); var responseDto = GetDto(responseObject); var dto = CustomResponseDto.Create; responseDto.string_value.Should().Be(dto.string_value); responseDto.int_value.Should().Be(dto.int_value); responseDto.nested.string_value.Should().Be(dto.nested.string_value); responseDto.nested.int_value.Should().Be(dto.nested.int_value); // token client response response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); // token content var payload = GetPayload(response); payload.Count().Should().Be(12); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.custom"); payload.Should().Contain("sub", "bob"); payload.Should().Contain("idp", "local"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("custom"); } [Fact] public async Task Extension_grant_failure_should_return_custom_error_response() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "outcome", "fail"} } }); var s = response.Json.ToString(); var fd = GetFieldsD(response); // raw fields var fields = GetFields(response); fields["string_value"].ToString().Should().Be("some_string"); fields["int_value"].GetInt64().Should().Be(42); JsonElement temp; fields.TryGetValue("identity_token", out temp).Should().BeFalse(); fields.TryGetValue("refresh_token", out temp).Should().BeFalse(); fields.TryGetValue("error", out temp).Should().BeTrue(); fields.TryGetValue("error_description", out temp).Should().BeTrue(); fields.TryGetValue("token_type", out temp).Should().BeFalse(); fields.TryGetValue("expires_in", out temp).Should().BeFalse(); var responseObject = fields["dto"]; responseObject.Should().NotBeNull(); var responseDto = GetDto(responseObject); var dto = CustomResponseDto.Create; responseDto.string_value.Should().Be(dto.string_value); responseDto.int_value.Should().Be(dto.int_value); responseDto.nested.string_value.Should().Be(dto.nested.string_value); responseDto.nested.int_value.Should().Be(dto.nested.int_value); // token client response response.IsError.Should().Be(true); response.Error.Should().Be("invalid_grant"); response.ErrorDescription.Should().Be("invalid_credential"); response.ExpiresIn.Should().Be(0); response.TokenType.Should().BeNull(); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); } private CustomResponseDto GetDto(JsonElement responseObject) { return responseObject.ToObject(); } private Dictionary GetFieldsD(TokenResponse response) { return response.Json.ToObject>(); } private Dictionary GetFields(TokenResponse response) { return GetFields(response.Json); } private Dictionary GetFields(JsonElement json) { return json.ToObject>(); } private Dictionary GetPayload(TokenResponse response) { var token = response.AccessToken.Split('.').Skip(1).Take(1).First(); var dictionary = JsonConvert.DeserializeObject>( Encoding.UTF8.GetString(Base64Url.Decode(token))); return dictionary; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/DiscoveryClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class DiscoveryClientTests { private const string DiscoveryEndpoint = "https://server/.well-known/openid-configuration"; private readonly HttpClient _client; public DiscoveryClientTests() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Discovery_document_should_have_expected_values() { var doc = await _client.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest { Address = DiscoveryEndpoint, Policy = { ValidateIssuerName = false } }); // endpoints doc.TokenEndpoint.Should().Be("https://server/connect/token"); doc.AuthorizeEndpoint.Should().Be("https://server/connect/authorize"); doc.IntrospectionEndpoint.Should().Be("https://server/connect/introspect"); doc.EndSessionEndpoint.Should().Be("https://server/connect/endsession"); // jwk doc.KeySet.Keys.Count.Should().Be(1); doc.KeySet.Keys.First().E.Should().NotBeNull(); doc.KeySet.Keys.First().N.Should().NotBeNull(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ExtensionGrantClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.IdentityModel.Tokens.Jwt; using System.Net; using System.Text; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class ExtensionGrantClient { private const string TokenEndpoint = "https://server/connect/token"; private readonly HttpClient _client; public ExtensionGrantClient() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Valid_client_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "custom_credential", "custom credential"}, { "scope", "api1" } } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); var unixNow = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var exp = Int64.Parse(payload["exp"].ToString()); exp.Should().BeLessThan(unixNow + 3605); exp.Should().BeGreaterThan(unixNow + 3595); payload.Count().Should().Be(12); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.custom"); payload.Should().Contain("sub", "818727"); payload.Should().Contain("idp", "local"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("custom"); } [Fact] public async Task Valid_client_with_extra_claim_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "custom_credential", "custom credential"}, { "extra_claim", "extra_value" }, { "scope", "api1" } } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); var unixNow = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var exp = Int64.Parse(payload["exp"].ToString()); exp.Should().BeLessThan(unixNow + 3605); exp.Should().BeGreaterThan(unixNow + 3595); payload.Count().Should().Be(13); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.custom"); payload.Should().Contain("sub", "818727"); payload.Should().Contain("idp", "local"); payload.Should().Contain("extra_claim", "extra_value"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("custom"); } [Fact] public async Task Valid_client_with_refreshed_extra_claim_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "custom_credential", "custom credential"}, { "extra_claim", "extra_value" }, { "scope", "api1 offline_access" } } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); var refreshResponse = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "client.custom", ClientSecret = "secret", RefreshToken = response.RefreshToken }); refreshResponse.IsError.Should().BeFalse(); refreshResponse.HttpStatusCode.Should().Be(HttpStatusCode.OK); refreshResponse.ExpiresIn.Should().Be(3600); refreshResponse.TokenType.Should().Be("Bearer"); refreshResponse.IdentityToken.Should().BeNull(); refreshResponse.RefreshToken.Should().NotBeNull(); var payload = GetPayload(refreshResponse); var unixNow = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var exp = Int64.Parse(payload["exp"].ToString()); exp.Should().BeLessThan(unixNow + 3605); exp.Should().BeGreaterThan(unixNow + 3595); payload.Count().Should().Be(13); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.custom"); payload.Should().Contain("sub", "818727"); payload.Should().Contain("idp", "local"); payload.Should().Contain("extra_claim", "extra_value"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("custom"); } [Fact] public async Task Valid_client_no_subject_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom.nosubject", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "custom_credential", "custom credential"}, { "scope", "api1" } } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(8); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.custom"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); } [Fact] public async Task Valid_client_with_default_scopes_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "custom_credential", "custom credential"} } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); var payload = GetPayload(response); payload.Count().Should().Be(12); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.custom"); payload.Should().Contain("sub", "818727"); payload.Should().Contain("idp", "local"); payload["aud"].Should().Be("api"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("custom"); var scopes = payload["scope"] as JArray; scopes.Count().Should().Be(3); scopes.First().ToString().Should().Be("api1"); scopes.Skip(1).First().ToString().Should().Be("api2"); scopes.Skip(2).First().ToString().Should().Be("offline_access"); } [Fact] public async Task Valid_client_missing_grant_specific_data_should_fail() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "scope", "api1" } } }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); response.ErrorDescription.Should().Be("invalid_custom_credential"); } [Fact] public async Task Valid_client_using_unsupported_grant_type_should_fail() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "invalid", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "custom_credential", "custom credential"}, { "scope", "api1" } } }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("unsupported_grant_type"); } [Fact] public async Task Valid_client_using_unauthorized_grant_type_should_fail() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "custom2", ClientId = "client.custom", ClientSecret = "secret", Parameters = { { "custom_credential", "custom credential"}, { "scope", "api1" } } }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("unsupported_grant_type"); } [Fact(Skip = "needs improvement")] public async Task Dynamic_lifetime_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "dynamic", ClientId = "client.dynamic", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "lifetime", "5000"}, { "sub", "818727"} } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(5000); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); var unixNow = DateTimeOffset.UtcNow.ToUnixTimeSeconds(); var exp = Int64.Parse(payload["exp"].ToString()); exp.Should().BeLessThan(unixNow + 5005); exp.Should().BeGreaterThan(unixNow + 4995); payload.Count().Should().Be(10); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.dynamic"); payload.Should().Contain("sub", "818727"); payload.Should().Contain("idp", "local"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("delegation"); } [Fact] public async Task Dynamic_token_type_jwt_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "dynamic", ClientId = "client.dynamic", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "type", "jwt"}, { "sub", "818727"} } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); response.AccessToken.Should().Contain("."); } [Fact] public async Task Impersonate_client_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "dynamic", ClientId = "client.dynamic", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "type", "jwt"}, { "impersonated_client", "impersonated_client_id"}, { "sub", "818727"} } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); response.AccessToken.Should().Contain("."); var jwt = new JwtSecurityToken(response.AccessToken); jwt.Payload["client_id"].Should().Be("impersonated_client_id"); } [Fact] public async Task Dynamic_token_type_reference_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "dynamic", ClientId = "client.dynamic", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "type", "reference"}, { "sub", "818727"} } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); response.AccessToken.Should().NotContain("."); } [Fact] public async Task Dynamic_client_claims_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "dynamic", ClientId = "client.dynamic", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "claim", "extra_claim"}, { "sub", "818727"} } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(13); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.dynamic"); payload.Should().Contain("sub", "818727"); payload.Should().Contain("idp", "local"); payload.Should().Contain("client_extra", "extra_claim"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("delegation"); } [Fact] public async Task Dynamic_client_claims_no_sub_should_succeed() { var response = await _client.RequestTokenAsync(new TokenRequest { Address = TokenEndpoint, GrantType = "dynamic", ClientId = "client.dynamic", ClientSecret = "secret", Parameters = { { "scope", "api1" }, { "claim", "extra_claim"}, } }); response.IsError.Should().BeFalse(); response.HttpStatusCode.Should().Be(HttpStatusCode.OK); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(9); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "client.dynamic"); payload.Should().Contain("client_extra", "extra_claim"); payload["aud"].Should().Be("api"); var scopes = payload["scope"] as JArray; scopes.First().ToString().Should().Be("api1"); } private Dictionary GetPayload(TokenResponse response) { var token = response.AccessToken.Split('.').Skip(1).Take(1).First(); var dictionary = JsonConvert.DeserializeObject>( Encoding.UTF8.GetString(Base64Url.Decode(token))); return dictionary; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/RefreshTokenClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class RefreshTokenClient { private const string TokenEndpoint = "https://server/connect/token"; private const string RevocationEndpoint = "https://server/connect/revocation"; private readonly HttpClient _client; public RefreshTokenClient() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Requesting_a_refresh_token_without_identity_scopes_should_return_expected_results() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "api1 offline_access", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", RefreshToken = response.RefreshToken }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); } [Fact] public async Task Requesting_a_refresh_token_with_identity_scopes_should_return_expected_results() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid api1 offline_access", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", RefreshToken = response.RefreshToken }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().NotBeNull(); response.RefreshToken.Should().NotBeNull(); } [Fact] public async Task Refreshing_a_refresh_token_with_reuse_should_return_same_refresh_token() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient.reuse", ClientSecret = "secret", Scope = "openid api1 offline_access", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); var rt1 = response.RefreshToken; response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient.reuse", ClientSecret = "secret", RefreshToken = response.RefreshToken }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().NotBeNull(); response.RefreshToken.Should().NotBeNull(); var rt2 = response.RefreshToken; rt1.Should().BeEquivalentTo(rt2); } [Fact] public async Task Refreshing_a_refresh_token_with_one_time_only_should_return_different_refresh_token() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid api1 offline_access", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); var rt1 = response.RefreshToken; response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", RefreshToken = response.RefreshToken }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().NotBeNull(); response.RefreshToken.Should().NotBeNull(); var rt2 = response.RefreshToken; rt1.Should().NotBeEquivalentTo(rt2); } [Fact] public async Task Replaying_a_rotated_token_should_fail() { // request initial token var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid api1 offline_access", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); var rt1 = response.RefreshToken; // refresh token response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", RefreshToken = response.RefreshToken }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().NotBeNull(); response.RefreshToken.Should().NotBeNull(); // refresh token (again) response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", RefreshToken = rt1 }); response.IsError.Should().BeTrue(); response.Error.Should().Be("invalid_grant"); } [Fact] public async Task Using_a_valid_refresh_token_should_succeed() { // request initial token var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid api1 offline_access", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); var rt1 = response.RefreshToken; // refresh token response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", RefreshToken = rt1 }); response.IsError.Should().BeFalse(); } [Fact] public async Task Using_a_revoked_refresh_token_should_fail() { // request initial token var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid api1 offline_access", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); var rt1 = response.RefreshToken; // revoke refresh token var revocationResponse = await _client.RevokeTokenAsync(new TokenRevocationRequest { Address = RevocationEndpoint, ClientId = "roclient", ClientSecret = "secret", Token = rt1, TokenTypeHint = "refresh_token" }); revocationResponse.IsError.Should().Be(false); // refresh token response = await _client.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", RefreshToken = rt1 }); response.IsError.Should().BeTrue(); response.Error.Should().Be("invalid_grant"); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/ResourceOwnerClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using System.Net; using System.Text; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class ResourceOwnerClient { private const string TokenEndpoint = "https://server/connect/token"; private readonly HttpClient _client; public ResourceOwnerClient() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Valid_user_should_succeed_with_expected_response_payload() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "api1", UserName = "bob", Password = "bob" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(12); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "roclient"); payload.Should().Contain("sub", "88421113"); payload.Should().Contain("idp", "local"); payload.Keys.Should().Contain("jti"); payload.Keys.Should().Contain("iat"); payload["aud"].Should().Be("api"); var scopes = ((JArray)payload["scope"]).Select(x => x.ToString()); scopes.Count().Should().Be(1); scopes.Should().Contain("api1"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("pwd"); } [Fact] public async Task Request_with_no_explicit_scopes_should_return_allowed_scopes() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", UserName = "bob", Password = "bob" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNull(); var payload = GetPayload(response); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "roclient"); payload.Should().Contain("sub", "88421113"); payload.Should().Contain("idp", "local"); payload["aud"].Should().Be("api"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("pwd"); var scopes = ((JArray)payload["scope"]).Select(x => x.ToString()); scopes.Count().Should().Be(8); // {[ "address", "api1", "api2", "api4.with.roles", "email", "offline_access", "openid", "role"]} scopes.Should().Contain("address"); scopes.Should().Contain("api1"); scopes.Should().Contain("api2"); scopes.Should().Contain("api4.with.roles"); scopes.Should().Contain("email"); scopes.Should().Contain("offline_access"); scopes.Should().Contain("openid"); scopes.Should().Contain("roles"); } [Fact] public async Task Request_containing_identity_scopes_should_return_expected_payload() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid email api1", UserName = "bob", Password = "bob" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().BeNull(); var payload = GetPayload(response); payload.Count().Should().Be(12); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "roclient"); payload.Should().Contain("sub", "88421113"); payload.Should().Contain("idp", "local"); payload.Keys.Should().Contain("jti"); payload.Keys.Should().Contain("iat"); payload["aud"].Should().Be("api"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("pwd"); var scopes = ((JArray)payload["scope"]).Select(x=>x.ToString()); scopes.Count().Should().Be(3); scopes.Should().Contain("api1"); scopes.Should().Contain("email"); scopes.Should().Contain("openid"); } [Fact] public async Task Request_for_refresh_token_should_return_expected_payload() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid email api1 offline_access", UserName = "bob", Password = "bob" }); response.IsError.Should().Be(false); response.ExpiresIn.Should().Be(3600); response.TokenType.Should().Be("Bearer"); response.IdentityToken.Should().BeNull(); response.RefreshToken.Should().NotBeNullOrWhiteSpace(); var payload = GetPayload(response); payload.Count().Should().Be(12); payload.Should().Contain("iss", "https://idsvr8"); payload.Should().Contain("client_id", "roclient"); payload.Should().Contain("sub", "88421113"); payload.Should().Contain("idp", "local"); payload.Keys.Should().Contain("jti"); payload.Keys.Should().Contain("iat"); payload["aud"].Should().Be("api"); var amr = payload["amr"] as JArray; amr.Count().Should().Be(1); amr.First().ToString().Should().Be("pwd"); var scopes = ((JArray)payload["scope"]).Select(x => x.ToString()); scopes.Count().Should().Be(4); scopes.Should().Contain("api1"); scopes.Should().Contain("email"); scopes.Should().Contain("offline_access"); scopes.Should().Contain("openid"); } [Fact] public async Task Unknown_user_should_fail() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "api1", UserName = "unknown", Password = "bob" }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_grant"); } [Fact] public async Task User_with_empty_password_should_succeed() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "api1", UserName = "bob_no_password" }); response.IsError.Should().Be(false); } [Theory] [InlineData("invalid")] [InlineData("")] public async Task User_with_invalid_password_should_fail(string password) { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "api1", UserName = "bob", Password = password }); response.IsError.Should().Be(true); response.ErrorType.Should().Be(ResponseErrorType.Protocol); response.HttpStatusCode.Should().Be(HttpStatusCode.BadRequest); response.Error.Should().Be("invalid_grant"); } private static Dictionary GetPayload(IdentityModel.Client.TokenResponse response) { var token = response.AccessToken.Split('.').Skip(1).Take(1).First(); var dictionary = JsonConvert.DeserializeObject>( Encoding.UTF8.GetString(Base64Url.Decode(token))); return dictionary; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/RevocationClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Net.Http; using System.Threading.Tasks; using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class RevocationClient { private const string TokenEndpoint = "https://server/connect/token"; private const string RevocationEndpoint = "https://server/connect/revocation"; private const string IntrospectionEndpoint = "https://server/connect/introspect"; private readonly HttpClient _client; public RevocationClient() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Revoking_reference_token_should_invalidate_token() { // request acccess token var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient.reference", ClientSecret = "secret", Scope = "api1", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); // introspect - should be active var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api", ClientSecret = "secret", Token = response.AccessToken }); introspectionResponse.IsActive.Should().Be(true); // revoke access token var revocationResponse = await _client.RevokeTokenAsync(new TokenRevocationRequest { Address = RevocationEndpoint, ClientId = "roclient.reference", ClientSecret = "secret", Token = response.AccessToken }); // introspect - should be inactive introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api", ClientSecret = "secret", Token = response.AccessToken }); introspectionResponse.IsActive.Should().Be(false); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/Clients.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using IdentityServer.IntegrationTests.Common; using IdentityServer8; using IdentityServer8.Models; namespace IdentityServer.IntegrationTests.Clients.Setup; internal class Clients { public static IEnumerable Get() { return new List { /////////////////////////////////////////// // Console Client Credentials Flow Sample ////////////////////////////////////////// new Client { ClientId = "client", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowOfflineAccess = true, AllowedScopes = { "api1", "api2", "other_api" } }, new Client { ClientId = "client.cnf", ClientSecrets = { new Secret { Type = "confirmation.test", Description = "Test for cnf claim", Value = "foo" } }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowOfflineAccess = true, AllowedScopes = { "api1", "api2" } }, new Client { ClientId = "client.and.ro", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPasswordAndClientCredentials, AllowedScopes = { "openid", "api1", "api2" } }, new Client { ClientId = "client.identityscopes", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "openid", "profile", "api1", "api2" } }, new Client { ClientId = "client.no_default_scopes", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials }, new Client { ClientId = "client.no_secret", AllowedGrantTypes = GrantTypes.ClientCredentials, RequireClientSecret = false, AllowedScopes = { "api1" } }, /////////////////////////////////////////// // Console Resource Owner Flow Sample ////////////////////////////////////////// new Client { ClientId = "roclient", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, RefreshTokenUsage = TokenUsage.OneTimeOnly, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Email, IdentityServerConstants.StandardScopes.Address, "roles", "api1", "api2", "api4.with.roles" } }, new Client { ClientId = "roclient.reuse", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = { IdentityServerConstants.StandardScopes.OpenId, IdentityServerConstants.StandardScopes.Email, IdentityServerConstants.StandardScopes.Address, "roles", "api1", "api2", "api4.with.roles" }, RefreshTokenUsage = TokenUsage.ReUse }, ///////////////////////////////////////// // Console Custom Grant Flow Sample //////////////////////////////////////// new Client { ClientId = "client.custom", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = { "custom", "custom.nosubject" }, AllowedScopes = { "api1", "api2" }, AllowOfflineAccess = true }, new Client { ClientId = "client.dynamic", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = { "dynamic" }, AllowedScopes = { "api1", "api2" }, AlwaysSendClientClaims = true }, /////////////////////////////////////////// // Introspection Client Sample ////////////////////////////////////////// new Client { ClientId = "roclient.reference", ClientSecrets = { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = { "api1", "api2" }, AccessTokenType = AccessTokenType.Reference }, new Client { ClientName = "Client with Base64 encoded X509 Certificate", ClientId = "certificate_base64_valid", Enabled = true, ClientSecrets = { new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = Convert.ToBase64String(TestCert.Load().Export(X509ContentType.Cert)) } }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = new List { "api1", "api2" } }, new Client { ClientId = "implicit", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = {"api1"}, RedirectUris = { "http://implicit" } }, new Client { ClientId = "implicit_and_client_creds", AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials, AllowedScopes = {"api1"}, RedirectUris = { "http://implicit_and_client_creds" } } }; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/ConfirmationSecretValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; using Newtonsoft.Json; namespace IdentityServer.IntegrationTests.Clients.Setup; public class ConfirmationSecretValidator : ISecretValidator { public Task ValidateAsync(IEnumerable secrets, ParsedSecret parsedSecret) { if (secrets.Any()) { if (secrets.First().Type == "confirmation.test") { var cnf = new Dictionary { { "x5t#S256", "foo" } }; var result = new SecretValidationResult { Success = true, Confirmation = JsonConvert.SerializeObject(cnf) }; return Task.FromResult(result); } } return Task.FromResult(new SecretValidationResult { Success = false }); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/CustomProfileService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Test; using Microsoft.Extensions.Logging; namespace IdentityServer.IntegrationTests.Clients.Setup; class CustomProfileService : TestUserProfileService { public CustomProfileService(TestUserStore users, ILogger logger) : base(users, logger) { } public override async Task GetProfileDataAsync(ProfileDataRequestContext context) { await base.GetProfileDataAsync(context); if (context.Subject.Identity.AuthenticationType == "custom") { var extraClaim = context.Subject.FindFirst("extra_claim"); if (extraClaim != null) { context.IssuedClaims.Add(extraClaim); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/CustomResponseDto.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer.IntegrationTests.Clients.Setup; public class CustomResponseDto { public string string_value { get; set; } public int int_value { get; set; } public CustomResponseDto nested { get; set; } public static CustomResponseDto Create { get { return new CustomResponseDto { string_value = "dto_string", int_value = 43, nested = new CustomResponseDto { string_value = "dto_nested_string", int_value = 44 } }; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/CustomResponseExtensionGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.IntegrationTests.Clients.Setup; public class CustomResponseExtensionGrantValidator : IExtensionGrantValidator { public Task ValidateAsync(ExtensionGrantValidationContext context) { var response = new Dictionary { { "string_value", "some_string" }, { "int_value", 42 }, { "dto", CustomResponseDto.Create } }; var credential = context.Request.Raw.Get("outcome"); if (credential == "succeed") { context.Result = new GrantValidationResult("bob", "custom", customResponse: response); } else { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid_credential", response); } return Task.CompletedTask; } public string GrantType { get { return "custom"; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/CustomResponseResourceOwnerValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.IntegrationTests.Clients.Setup; public class CustomResponseResourceOwnerValidator : IResourceOwnerPasswordValidator { public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { var response = new Dictionary { { "string_value", "some_string" }, { "int_value", 42 }, { "dto", CustomResponseDto.Create } }; if (context.UserName == context.Password) { context.Result = new GrantValidationResult(context.UserName, "password", customResponse: response); } else { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid_credential", response); } return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/DynamicParameterExtensionGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Security.Claims; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.IntegrationTests.Clients.Setup; public class DynamicParameterExtensionGrantValidator : IExtensionGrantValidator { public Task ValidateAsync(ExtensionGrantValidationContext context) { var impersonatedClient = context.Request.Raw.Get("impersonated_client"); var lifetime = context.Request.Raw.Get("lifetime"); var extraClaim = context.Request.Raw.Get("claim"); var tokenType = context.Request.Raw.Get("type"); var sub = context.Request.Raw.Get("sub"); if (!string.IsNullOrEmpty(impersonatedClient)) { context.Request.ClientId = impersonatedClient; } if (!string.IsNullOrEmpty(lifetime)) { context.Request.AccessTokenLifetime = int.Parse(lifetime); } if (!string.IsNullOrEmpty(tokenType)) { if (tokenType == "jwt") { context.Request.AccessTokenType = AccessTokenType.Jwt; } else if (tokenType == "reference") { context.Request.AccessTokenType = AccessTokenType.Reference; } } if (!string.IsNullOrEmpty(extraClaim)) { context.Request.ClientClaims.Add(new Claim("extra", extraClaim)); } if (!string.IsNullOrEmpty(sub)) { context.Result = new GrantValidationResult(sub, "delegation"); } else { context.Result = new GrantValidationResult(); } return Task.CompletedTask; } public string GrantType => "dynamic"; } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/ExtensionGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Security.Claims; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.IntegrationTests.Clients.Setup; public class ExtensionGrantValidator : IExtensionGrantValidator { public Task ValidateAsync(ExtensionGrantValidationContext context) { var credential = context.Request.Raw.Get("custom_credential"); var extraClaim = context.Request.Raw.Get("extra_claim"); if (credential != null) { if (extraClaim != null) { context.Result = new GrantValidationResult( subject: "818727", claims: new[] { new Claim("extra_claim", extraClaim) }, authenticationMethod: GrantType); } else { context.Result = new GrantValidationResult(subject: "818727", authenticationMethod: GrantType); } } else { // custom error message context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid_custom_credential"); } return Task.CompletedTask; } public string GrantType => "custom"; } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/ExtensionGrantValidator2.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Validation; namespace IdentityServer.IntegrationTests.Clients.Setup; public class ExtensionGrantValidator2 : IExtensionGrantValidator { public Task ValidateAsync(ExtensionGrantValidationContext context) { var credential = context.Request.Raw.Get("custom_credential"); if (credential != null) { // valid credential context.Result = new GrantValidationResult("818727", "custom"); } else { // custom error message context.Result = new GrantValidationResult(IdentityServer8.Models.TokenRequestErrors.InvalidGrant, "invalid custom credential"); } return Task.CompletedTask; } public string GrantType => "custom2"; } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/NoSubjectExtensionGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.IntegrationTests.Clients.Setup; public class NoSubjectExtensionGrantValidator : IExtensionGrantValidator { public Task ValidateAsync(ExtensionGrantValidationContext context) { var credential = context.Request.Raw.Get("custom_credential"); if (credential != null) { context.Result = new GrantValidationResult(); } else { // custom error message context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, "invalid custom credential"); } return Task.CompletedTask; } public string GrantType => "custom.nosubject"; } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/Scopes.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using IdentityServer8.Models; namespace IdentityServer.IntegrationTests.Clients.Setup; internal class Scopes { public static IEnumerable GetIdentityScopes() { return new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Email(), new IdentityResources.Address(), new IdentityResource("roles", new[] { "role" }) }; } public static IEnumerable GetApiResources() { return new List { new ApiResource { Name = "api", ApiSecrets = { new Secret("secret".Sha256()) }, Scopes = { "api1", "api2", "api3", "api4.with.roles" } }, new ApiResource("other_api") { Scopes = { "other_api" } } }; } public static IEnumerable GetApiScopes() { return new ApiScope[] { new ApiScope { Name = "api1" }, new ApiScope { Name = "api2" }, new ApiScope { Name = "api3" }, new ApiScope { Name = "api4.with.roles", UserClaims = { "role" } }, new ApiScope { Name = "other_api", }, }; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Configuration; using IdentityServer8.Validation; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace IdentityServer.IntegrationTests.Clients.Setup; public class Startup { static public ICustomTokenRequestValidator CustomTokenRequestValidator { get; set; } public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(); var builder = services.AddIdentityServer(options => { options.IssuerUri = "https://idsvr8"; options.Events = new EventsOptions { RaiseErrorEvents = true, RaiseFailureEvents = true, RaiseInformationEvents = true, RaiseSuccessEvents = true }; }); builder.AddInMemoryClients(Clients.Get()); builder.AddInMemoryIdentityResources(Scopes.GetIdentityScopes()); builder.AddInMemoryApiResources(Scopes.GetApiResources()); builder.AddInMemoryApiScopes(Scopes.GetApiScopes()); builder.AddTestUsers(Users.Get()); builder.AddDeveloperSigningCredential(persistKey: false); builder.AddExtensionGrantValidator(); builder.AddExtensionGrantValidator(); builder.AddExtensionGrantValidator(); builder.AddExtensionGrantValidator(); builder.AddProfileService(); builder.AddJwtBearerClientAuthentication(); builder.AddSecretValidator(); // add a custom token request validator if set if (CustomTokenRequestValidator != null) { builder.Services.AddTransient(r => CustomTokenRequestValidator); } } public void Configure(IApplicationBuilder app) { app.UseIdentityServer(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/StartupWithCustomTokenResponses.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Configuration; using IdentityServer8.Validation; using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; namespace IdentityServer.IntegrationTests.Clients.Setup; public class StartupWithCustomTokenResponses { public void ConfigureServices(IServiceCollection services) { services.AddAuthentication(); var builder = services.AddIdentityServer(options => { options.IssuerUri = "https://idsvr8"; options.Events = new EventsOptions { RaiseErrorEvents = true, RaiseFailureEvents = true, RaiseInformationEvents = true, RaiseSuccessEvents = true }; }); builder.AddInMemoryClients(Clients.Get()); builder.AddInMemoryIdentityResources(Scopes.GetIdentityScopes()); builder.AddInMemoryApiResources(Scopes.GetApiResources()); builder.AddInMemoryApiScopes(Scopes.GetApiScopes()); builder.AddDeveloperSigningCredential(persistKey: false); services.AddTransient(); builder.AddExtensionGrantValidator(); } public void Configure(IApplicationBuilder app) { app.UseIdentityServer(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/TestCustomTokenRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Threading.Tasks; using IdentityServer8.Validation; namespace IdentityServer.IntegrationTests.Clients.Setup; public class TestCustomTokenRequestValidator : ICustomTokenRequestValidator { public Task ValidateAsync(CustomTokenRequestValidationContext context) { context.Result.CustomResponse = new Dictionary { {"custom", "custom" } }; return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/Setup/Users.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Security.Claims; using IdentityModel; using IdentityServer8; using IdentityServer8.Test; namespace IdentityServer.IntegrationTests.Clients.Setup; internal static class Users { public static List Get() { var users = new List { new TestUser{SubjectId = "818727", Username = "alice", Password = "alice", Claims = new Claim[] { new Claim(JwtClaimTypes.Name, "Alice Smith"), new Claim(JwtClaimTypes.GivenName, "Alice"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "AliceSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.Role, "Admin"), new Claim(JwtClaimTypes.Role, "Geek"), new Claim(JwtClaimTypes.WebSite, "http://alice.com"), new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServerConstants.ClaimValueTypes.Json) } }, new TestUser{SubjectId = "88421113", Username = "bob", Password = "bob", Claims = new Claim[] { new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.Role, "Developer"), new Claim(JwtClaimTypes.Role, "Geek"), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServerConstants.ClaimValueTypes.Json) } }, new TestUser{SubjectId = "88421113", Username = "bob_no_password", Claims = new Claim[] { new Claim(JwtClaimTypes.Name, "Bob Smith"), new Claim(JwtClaimTypes.GivenName, "Bob"), new Claim(JwtClaimTypes.FamilyName, "Smith"), new Claim(JwtClaimTypes.Email, "BobSmith@email.com"), new Claim(JwtClaimTypes.EmailVerified, "true", ClaimValueTypes.Boolean), new Claim(JwtClaimTypes.Role, "Developer"), new Claim(JwtClaimTypes.Role, "Geek"), new Claim(JwtClaimTypes.WebSite, "http://bob.com"), new Claim(JwtClaimTypes.Address, @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServerConstants.ClaimValueTypes.Json) } } }; return users; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Clients/UserInfoClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityModel.Client; using IdentityServer.IntegrationTests.Clients.Setup; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using Xunit; namespace IdentityServer.IntegrationTests.Clients; public class UserInfoEndpointClient { private const string TokenEndpoint = "https://server/connect/token"; private const string UserInfoEndpoint = "https://server/connect/userinfo"; private readonly HttpClient _client; public UserInfoEndpointClient() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _client = server.CreateClient(); } [Fact] public async Task Valid_client_with_GET_should_succeed() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid email api1", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); var userInfo = await _client.GetUserInfoAsync(new UserInfoRequest { Address = UserInfoEndpoint, Token = response.AccessToken }); userInfo.IsError.Should().BeFalse(); userInfo.Claims.Count().Should().Be(3); userInfo.Claims.Should().Contain(c => c.Type == "sub" && c.Value == "88421113"); userInfo.Claims.Should().Contain(c => c.Type == "email" && c.Value == "BobSmith@email.com"); userInfo.Claims.Should().Contain(c => c.Type == "email_verified" && c.Value == "true"); } [Fact] public async Task Request_address_scope_should_return_expected_response() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid address", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); var userInfo = await _client.GetUserInfoAsync(new UserInfoRequest { Address = UserInfoEndpoint, Token = response.AccessToken }); userInfo.IsError.Should().BeFalse(); userInfo.Claims.First().Value.Should().Be("{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }"); } [Fact] public async Task Using_a_token_with_no_identity_scope_should_fail() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "api1", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); var userInfo = await _client.GetUserInfoAsync(new UserInfoRequest { Address = UserInfoEndpoint, Token = response.AccessToken }); userInfo.IsError.Should().BeTrue(); userInfo.HttpStatusCode.Should().Be(HttpStatusCode.Forbidden); } [Fact] public async Task Using_a_token_with_an_identity_scope_but_no_openid_should_fail() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "email api1", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); var userInfo = await _client.GetUserInfoAsync(new UserInfoRequest { Address = UserInfoEndpoint, Token = response.AccessToken }); userInfo.IsError.Should().BeTrue(); userInfo.HttpStatusCode.Should().Be(HttpStatusCode.Forbidden); } [Fact] public async Task Invalid_token_should_fail() { var userInfo = await _client.GetUserInfoAsync(new UserInfoRequest { Address = UserInfoEndpoint, Token = "invalid" }); userInfo.IsError.Should().BeTrue(); userInfo.HttpStatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] public async Task Complex_json_should_be_correct() { var response = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "roclient", ClientSecret = "secret", Scope = "openid email api1 api4.with.roles roles", UserName = "bob", Password = "bob" }); response.IsError.Should().BeFalse(); var payload = GetPayload(response); var scopes = ((JArray) payload["scope"]).Select(x => x.ToString()).ToArray(); scopes.Length.Should().Be(5); scopes.Should().Contain("openid"); scopes.Should().Contain("email"); scopes.Should().Contain("api1"); scopes.Should().Contain("api4.with.roles"); scopes.Should().Contain("roles"); var roles = ((JArray) payload["role"]).Select(x => x.ToString()).ToArray(); roles.Length.Should().Be(2); roles.Should().Contain("Geek"); roles.Should().Contain("Developer"); var userInfo = await _client.GetUserInfoAsync(new UserInfoRequest { Address = UserInfoEndpoint, Token = response.AccessToken }); //roles = ((JArray)userInfo.Json["role"]).Select(x => x.ToString()).ToArray(); roles = userInfo.Json.TryGetStringArray("role").ToArray(); roles.Length.Should().Be(2); roles.Should().Contain("Geek"); roles.Should().Contain("Developer"); } private Dictionary GetPayload(TokenResponse response) { var token = response.AccessToken.Split('.').Skip(1).Take(1).First(); var dictionary = JsonConvert.DeserializeObject>( Encoding.UTF8.GetString(Base64Url.Decode(token))); return dictionary; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Common/BrowserClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Net.Http; namespace IdentityServer.IntegrationTests.Common; public class BrowserClient : HttpClient { public BrowserClient(BrowserHandler browserHandler) : base(browserHandler) { BrowserHandler = browserHandler; } public BrowserHandler BrowserHandler { get; private set; } public bool AllowCookies { get { return BrowserHandler.AllowCookies; } set { BrowserHandler.AllowCookies = value; } } public bool AllowAutoRedirect { get { return BrowserHandler.AllowAutoRedirect; } set { BrowserHandler.AllowAutoRedirect = value; } } public int ErrorRedirectLimit { get { return BrowserHandler.ErrorRedirectLimit; } set { BrowserHandler.ErrorRedirectLimit = value; } } public int StopRedirectingAfter { get { return BrowserHandler.StopRedirectingAfter; } set { BrowserHandler.StopRedirectingAfter = value; } } internal void RemoveCookie(string uri, string name) { BrowserHandler.RemoveCookie(uri, name); } internal System.Net.Cookie GetCookie(string uri, string name) { return BrowserHandler.GetCookie(uri, name); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Common/BrowserHandler.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Linq; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace IdentityServer.IntegrationTests.Common; // thanks to Damian Hickey for this awesome sample // https://github.com/damianh/OwinHttpMessageHandler/blob/master/src/OwinHttpMessageHandler/OwinHttpMessageHandler.cs public class BrowserHandler : DelegatingHandler { private CookieContainer _cookieContainer = new CookieContainer(); public bool AllowCookies { get; set; } = true; public bool AllowAutoRedirect { get; set; } = true; public int ErrorRedirectLimit { get; set; } = 20; public int StopRedirectingAfter { get; set; } = Int32.MaxValue; public BrowserHandler(HttpMessageHandler next) : base(next) { } protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { var response = await SendCookiesAsync(request, cancellationToken); int redirectCount = 0; while (AllowAutoRedirect && (300 <= (int)response.StatusCode && (int)response.StatusCode < 400) && redirectCount < StopRedirectingAfter) { if (redirectCount >= ErrorRedirectLimit) { throw new InvalidOperationException(string.Format("Too many redirects. Error limit = {0}", redirectCount)); } var location = response.Headers.Location; if (!location.IsAbsoluteUri) { location = new Uri(response.RequestMessage.RequestUri, location); } request = new HttpRequestMessage(HttpMethod.Get, location); response = await SendCookiesAsync(request, cancellationToken).ConfigureAwait(false); redirectCount++; } return response; } internal Cookie GetCookie(string uri, string name) { return _cookieContainer.GetCookies(new Uri(uri)).Cast().Where(x => x.Name == name).FirstOrDefault(); } internal void RemoveCookie(string uri, string name) { var cookie = _cookieContainer.GetCookies(new Uri(uri)).Cast().Where(x=>x.Name == name).FirstOrDefault(); if (cookie != null) { cookie.Expired = true; } } protected async Task SendCookiesAsync(HttpRequestMessage request, CancellationToken cancellationToken) { if (AllowCookies) { string cookieHeader = _cookieContainer.GetCookieHeader(request.RequestUri); if (!string.IsNullOrEmpty(cookieHeader)) { request.Headers.Add("Cookie", cookieHeader); } } var response = await base.SendAsync(request, cancellationToken); if (AllowCookies && response.Headers.Contains("Set-Cookie")) { var responseCookieHeader = string.Join(",", response.Headers.GetValues("Set-Cookie")); _cookieContainer.SetCookies(request.RequestUri, responseCookieHeader); } return response; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Common/IdentityServerPipeline.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using IdentityModel.Client; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Test; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Builder; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.TestHost; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace IdentityServer.IntegrationTests.Common; public class IdentityServerPipeline { public const string BaseUrl = "https://server"; public const string LoginPage = BaseUrl + "/account/login"; public const string ConsentPage = BaseUrl + "/account/consent"; public const string ErrorPage = BaseUrl + "/home/error"; public const string DeviceAuthorization = BaseUrl + "/connect/deviceauthorization"; public const string DiscoveryEndpoint = BaseUrl + "/.well-known/openid-configuration"; public const string DiscoveryKeysEndpoint = BaseUrl + "/.well-known/openid-configuration/jwks"; public const string AuthorizeEndpoint = BaseUrl + "/connect/authorize"; public const string TokenEndpoint = BaseUrl + "/connect/token"; public const string RevocationEndpoint = BaseUrl + "/connect/revocation"; public const string UserInfoEndpoint = BaseUrl + "/connect/userinfo"; public const string IntrospectionEndpoint = BaseUrl + "/connect/introspect"; public const string IdentityTokenValidationEndpoint = BaseUrl + "/connect/identityTokenValidation"; public const string EndSessionEndpoint = BaseUrl + "/connect/endsession"; public const string EndSessionCallbackEndpoint = BaseUrl + "/connect/endsession/callback"; public const string CheckSessionEndpoint = BaseUrl + "/connect/checksession"; public const string FederatedSignOutPath = "/signout-oidc"; public const string FederatedSignOutUrl = BaseUrl + FederatedSignOutPath; public IdentityServerOptions Options { get; set; } public List Clients { get; set; } = new List(); public List IdentityScopes { get; set; } = new List(); public List ApiResources { get; set; } = new List(); public List ApiScopes { get; set; } = new List(); public List Users { get; set; } = new List(); public TestServer Server { get; set; } public HttpMessageHandler Handler { get; set; } public BrowserClient BrowserClient { get; set; } public HttpClient BackChannelClient { get; set; } public MockMessageHandler BackChannelMessageHandler { get; set; } = new MockMessageHandler(); public MockMessageHandler JwtRequestMessageHandler { get; set; } = new MockMessageHandler(); public event Action OnPreConfigureServices = services => { }; public event Action OnPostConfigureServices = services => { }; public event Action OnPreConfigure = app => { }; public event Action OnPostConfigure = app => { }; public Func> OnFederatedSignout; public void Initialize(string basePath = null, bool enableLogging = false) { var builder = new WebHostBuilder(); builder.ConfigureServices(ConfigureServices); builder.Configure(app => { if (basePath != null) { app.Map(basePath, map => { ConfigureApp(map); }); } else { ConfigureApp(app); } }); if (enableLogging) { builder.ConfigureLogging((ctx, b) => b.AddConsole()); } Server = new TestServer(builder); Handler = Server.CreateHandler(); BrowserClient = new BrowserClient(new BrowserHandler(Handler)); BackChannelClient = new HttpClient(Handler); } public void ConfigureServices(IServiceCollection services) { OnPreConfigureServices(services); services.AddAuthentication(opts => { opts.AddScheme("external", scheme => { scheme.DisplayName = "External"; scheme.HandlerType = typeof(MockExternalAuthenticationHandler); }); }); services.AddTransient(svcs => { var handler = new MockExternalAuthenticationHandler(svcs.GetRequiredService()); if (OnFederatedSignout != null) handler.OnFederatedSignout = OnFederatedSignout; return handler; }); services.AddIdentityServer(options => { Options = options; options.Events = new EventsOptions { RaiseErrorEvents = true, RaiseFailureEvents = true, RaiseInformationEvents = true, RaiseSuccessEvents = true }; }) .AddInMemoryClients(Clients) .AddInMemoryIdentityResources(IdentityScopes) .AddInMemoryApiResources(ApiResources) .AddInMemoryApiScopes(ApiScopes) .AddTestUsers(Users) .AddDeveloperSigningCredential(persistKey: false); services.AddHttpClient(IdentityServerConstants.HttpClients.BackChannelLogoutHttpClient) .AddHttpMessageHandler(() => BackChannelMessageHandler); services.AddHttpClient(IdentityServerConstants.HttpClients.JwtRequestUriHttpClient) .AddHttpMessageHandler(() => JwtRequestMessageHandler); OnPostConfigureServices(services); } public void ConfigureApp(IApplicationBuilder app) { OnPreConfigure(app); app.UseIdentityServer(); // UI endpoints app.Map(Constants.UIConstants.DefaultRoutePaths.Login.EnsureLeadingSlash(), path => { path.Run(ctx => OnLogin(ctx)); }); app.Map(Constants.UIConstants.DefaultRoutePaths.Logout.EnsureLeadingSlash(), path => { path.Run(ctx => OnLogout(ctx)); }); app.Map(Constants.UIConstants.DefaultRoutePaths.Consent.EnsureLeadingSlash(), path => { path.Run(ctx => OnConsent(ctx)); }); app.Map(Constants.UIConstants.DefaultRoutePaths.Error.EnsureLeadingSlash(), path => { path.Run(ctx => OnError(ctx)); }); OnPostConfigure(app); } public bool LoginWasCalled { get; set; } public AuthorizationRequest LoginRequest { get; set; } public ClaimsPrincipal Subject { get; set; } public bool FollowLoginReturnUrl { get; set; } private async Task OnLogin(HttpContext ctx) { LoginWasCalled = true; await ReadLoginRequest(ctx); await IssueLoginCookie(ctx); } private async Task ReadLoginRequest(HttpContext ctx) { var interaction = ctx.RequestServices.GetRequiredService(); LoginRequest = await interaction.GetAuthorizationContextAsync(ctx.Request.Query["returnUrl"].FirstOrDefault()); } private async Task IssueLoginCookie(HttpContext ctx) { if (Subject != null) { var props = new AuthenticationProperties(); await ctx.SignInAsync(Subject, props); Subject = null; var url = ctx.Request.Query[Options.UserInteraction.LoginReturnUrlParameter].FirstOrDefault(); if (url != null) { ctx.Response.RedirectIfAllowed(url); } } } public bool LogoutWasCalled { get; set; } public LogoutRequest LogoutRequest { get; set; } private async Task OnLogout(HttpContext ctx) { LogoutWasCalled = true; await ReadLogoutRequest(ctx); await ctx.SignOutAsync(); } private async Task ReadLogoutRequest(HttpContext ctx) { var interaction = ctx.RequestServices.GetRequiredService(); LogoutRequest = await interaction.GetLogoutContextAsync(ctx.Request.Query["logoutId"].FirstOrDefault()); } public bool ConsentWasCalled { get; set; } public AuthorizationRequest ConsentRequest { get; set; } public ConsentResponse ConsentResponse { get; set; } private async Task OnConsent(HttpContext ctx) { ConsentWasCalled = true; await ReadConsentMessage(ctx); await CreateConsentResponse(ctx); } private async Task ReadConsentMessage(HttpContext ctx) { var interaction = ctx.RequestServices.GetRequiredService(); ConsentRequest = await interaction.GetAuthorizationContextAsync(ctx.Request.Query["returnUrl"].FirstOrDefault()); } private async Task CreateConsentResponse(HttpContext ctx) { if (ConsentRequest != null && ConsentResponse != null) { var interaction = ctx.RequestServices.GetRequiredService(); await interaction.GrantConsentAsync(ConsentRequest, ConsentResponse); ConsentResponse = null; var url = ctx.Request.Query[Options.UserInteraction.ConsentReturnUrlParameter].FirstOrDefault(); if (url != null) { ctx.Response.RedirectIfAllowed(url); } } } public bool ErrorWasCalled { get; set; } public ErrorMessage ErrorMessage { get; set; } private async Task OnError(HttpContext ctx) { ErrorWasCalled = true; await ReadErrorMessage(ctx); } private async Task ReadErrorMessage(HttpContext ctx) { var interaction = ctx.RequestServices.GetRequiredService(); ErrorMessage = await interaction.GetErrorContextAsync(ctx.Request.Query["errorId"].FirstOrDefault()); } /* helpers */ public async Task LoginAsync(ClaimsPrincipal subject) { var old = BrowserClient.AllowAutoRedirect; BrowserClient.AllowAutoRedirect = false; Subject = subject; await BrowserClient.GetAsync(LoginPage); BrowserClient.AllowAutoRedirect = old; } public async Task LoginAsync(string subject) { await LoginAsync(new IdentityServerUser(subject).CreatePrincipal()); } public void RemoveLoginCookie() { BrowserClient.RemoveCookie(BaseUrl, IdentityServerConstants.DefaultCookieAuthenticationScheme); } public void RemoveSessionCookie() { BrowserClient.RemoveCookie(BaseUrl, IdentityServerConstants.DefaultCheckSessionCookieName); } public Cookie GetSessionCookie() { return BrowserClient.GetCookie(BaseUrl, IdentityServerConstants.DefaultCheckSessionCookieName); } public string CreateAuthorizeUrl( string clientId = null, string responseType = null, string scope = null, string redirectUri = null, string state = null, string nonce = null, string loginHint = null, string acrValues = null, string responseMode = null, string codeChallenge = null, string codeChallengeMethod = null, object extra = null) { Parameters prms = extra is null ? null : Parameters.FromObject(extra); var url = new RequestUrl(AuthorizeEndpoint).CreateAuthorizeUrl( clientId: clientId, responseType: responseType, scope: scope, redirectUri: redirectUri, state: state, nonce: nonce, loginHint: loginHint, acrValues: acrValues, responseMode: responseMode, codeChallenge: codeChallenge, codeChallengeMethod: codeChallengeMethod, extra: prms); return url; } public AuthorizeResponse ParseAuthorizationResponseUrl(string url) { return new AuthorizeResponse(url); } public async Task RequestAuthorizationEndpointAsync( string clientId, string responseType, string scope = null, string redirectUri = null, string state = null, string nonce = null, string loginHint = null, string acrValues = null, string responseMode = null, string codeChallenge = null, string codeChallengeMethod = null, object extra = null) { var old = BrowserClient.AllowAutoRedirect; BrowserClient.AllowAutoRedirect = false; var url = CreateAuthorizeUrl(clientId, responseType, scope, redirectUri, state, nonce, loginHint, acrValues, responseMode, codeChallenge, codeChallengeMethod, extra); var result = await BrowserClient.GetAsync(url); result.StatusCode.Should().Be(HttpStatusCode.Found); BrowserClient.AllowAutoRedirect = old; var redirect = result.Headers.Location.ToString(); if (redirect.StartsWith(IdentityServerPipeline.ErrorPage)) { // request error page in pipeline so we can get error info await BrowserClient.GetAsync(redirect); // no redirect to client return null; } return new AuthorizeResponse(redirect); } } public class MockMessageHandler : DelegatingHandler { public bool InvokeWasCalled { get; set; } public Func OnInvoke { get; set; } public HttpResponseMessage Response { get; set; } = new HttpResponseMessage(HttpStatusCode.OK); protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { InvokeWasCalled = true; if (OnInvoke != null) { await OnInvoke.Invoke(request); } return Response; } } public class MockExternalAuthenticationHandler : IAuthenticationHandler, IAuthenticationSignInHandler, IAuthenticationRequestHandler { private readonly IHttpContextAccessor _httpContextAccessor; private HttpContext HttpContext => _httpContextAccessor.HttpContext; public Func> OnFederatedSignout = async context => { await context.SignOutAsync(); return true; }; public MockExternalAuthenticationHandler(IHttpContextAccessor httpContextAccessor) { _httpContextAccessor = httpContextAccessor; } public async Task HandleRequestAsync() { if (HttpContext.Request.Path == IdentityServerPipeline.FederatedSignOutPath) { return await OnFederatedSignout(HttpContext); } return false; } public Task AuthenticateAsync() { return Task.FromResult(AuthenticateResult.NoResult()); } public Task ChallengeAsync(AuthenticationProperties properties) { return Task.CompletedTask; } public Task ForbidAsync(AuthenticationProperties properties) { return Task.CompletedTask; } public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) { return Task.CompletedTask; } public Task SignInAsync(ClaimsPrincipal user, AuthenticationProperties properties) { return Task.CompletedTask; } public Task SignOutAsync(AuthenticationProperties properties) { return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Common/MessageHandlerWrapper.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace IdentityServer.IntegrationTests.Common; public class MessageHandlerWrapper : DelegatingHandler { public HttpResponseMessage Response { get; set; } public MessageHandlerWrapper(HttpMessageHandler handler) : base(handler) { } protected async override Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Response = await base.SendAsync(request, cancellationToken); return Response; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Common/NetworkHandler.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace IdentityServer.IntegrationTests.Common; public class NetworkHandler : HttpMessageHandler { enum Behavior { Throw, ReturnError, ReturnDocument } private readonly Exception _exception; private readonly Behavior _behavior; private readonly HttpStatusCode _statusCode; private readonly string _reason; private readonly string _document; private readonly Func _selector; private readonly Func _action; public HttpRequestMessage Request { get; set; } public string Body { get; set; } public NetworkHandler(Exception exception) { _exception = exception; _behavior = Behavior.Throw; } public NetworkHandler(HttpStatusCode statusCode, string reason) { _statusCode = statusCode; _reason = reason; _behavior = Behavior.ReturnError; } public NetworkHandler(string document, HttpStatusCode statusCode) { _statusCode = statusCode; _document = document; _behavior = Behavior.ReturnDocument; } public NetworkHandler(Func documentSelector, HttpStatusCode statusCode) { _statusCode = statusCode; _selector = documentSelector; _behavior = Behavior.ReturnDocument; } public NetworkHandler(Func action) { _action = action; } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Request = request; Body = await SafeReadContentFrom(request); if (_action != null) { return _action(request); } if (_behavior == Behavior.Throw) throw _exception; var response = new HttpResponseMessage(_statusCode); if (_behavior == Behavior.ReturnError) { response.ReasonPhrase = _reason; } if (_behavior == Behavior.ReturnDocument) { if (_selector != null) { response.Content = new StringContent(_selector(request)); } else { response.Content = new StringContent(_document); } } return response; } private async Task SafeReadContentFrom(HttpRequestMessage request) { if (request.Content == null) return null; return await request.Content.ReadAsStringAsync(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Common/TestCert.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IO; using System.Security.Cryptography.X509Certificates; namespace IdentityServer.IntegrationTests.Common; internal static class TestCert { public static X509Certificate2 Load() { var cert = Path.Combine(System.AppContext.BaseDirectory, "identityserver_testing.pfx"); return new X509Certificate2(cert, "password"); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/ClientAuthenticationTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Conformance.Basic; public class ClientAuthenticationTests { private const string Category = "Conformance.Basic.ClientAuthenticationTests"; private IdentityServerPipeline _pipeline = new IdentityServerPipeline(); public ClientAuthenticationTests() { _pipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _pipeline.Clients.Add(new Client { Enabled = true, ClientId = "code_pipeline.Client", ClientSecrets = new List { new Secret("secret".Sha512()) }, AllowedGrantTypes = GrantTypes.Code, AllowedScopes = { "openid" }, RequireConsent = false, RequirePkce = false, RedirectUris = new List { "https://code_pipeline.Client/callback", "https://code_pipeline.Client/callback?foo=bar&baz=quux" } }); _pipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _pipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task Token_endpoint_supports_client_authentication_with_basic_authentication_with_POST() { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); _pipeline.BrowserClient.AllowAutoRedirect = false; var url = _pipeline.CreateAuthorizeUrl( clientId: "code_pipeline.Client", responseType: "code", scope: "openid", redirectUri: "https://code_pipeline.Client/callback?foo=bar&baz=quux", nonce: nonce); var response = await _pipeline.BrowserClient.GetAsync(url); var authorization = _pipeline.ParseAuthorizationResponseUrl(response.Headers.Location.ToString()); authorization.Code.Should().NotBeNull(); var code = authorization.Code; // backchannel client var wrapper = new MessageHandlerWrapper(_pipeline.Handler); var tokenClient = new HttpClient(wrapper); var tokenResult = await tokenClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = "code_pipeline.Client", ClientSecret = "secret", Code = code, RedirectUri = "https://code_pipeline.Client/callback?foo=bar&baz=quux" }); tokenResult.IsError.Should().BeFalse(); tokenResult.HttpErrorReason.Should().Be("OK"); tokenResult.TokenType.Should().Be("Bearer"); tokenResult.AccessToken.Should().NotBeNull(); tokenResult.ExpiresIn.Should().BeGreaterThan(0); tokenResult.IdentityToken.Should().NotBeNull(); wrapper.Response.Headers.CacheControl.NoCache.Should().BeTrue(); wrapper.Response.Headers.CacheControl.NoStore.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Token_endpoint_supports_client_authentication_with_form_encoded_authentication_in_POST_body() { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); _pipeline.BrowserClient.AllowAutoRedirect = false; var url = _pipeline.CreateAuthorizeUrl( clientId: "code_pipeline.Client", responseType: "code", scope: "openid", redirectUri: "https://code_pipeline.Client/callback?foo=bar&baz=quux", nonce: nonce); var response = await _pipeline.BrowserClient.GetAsync(url); var authorization = _pipeline.ParseAuthorizationResponseUrl(response.Headers.Location.ToString()); authorization.Code.Should().NotBeNull(); var code = authorization.Code; // backchannel client var wrapper = new MessageHandlerWrapper(_pipeline.Handler); var tokenClient = new HttpClient(wrapper); var tokenResult = await tokenClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = "code_pipeline.Client", ClientSecret = "secret", ClientCredentialStyle = ClientCredentialStyle.PostBody, Code = code, RedirectUri = "https://code_pipeline.Client/callback?foo=bar&baz=quux" }); tokenResult.IsError.Should().BeFalse(); tokenResult.HttpErrorReason.Should().Be("OK"); tokenResult.TokenType.Should().Be("Bearer"); tokenResult.AccessToken.Should().NotBeNull(); tokenResult.ExpiresIn.Should().BeGreaterThan(0); tokenResult.IdentityToken.Should().NotBeNull(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/CodeFlowTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Conformance.Basic; public class CodeFlowTests { private const string Category = "Conformance.Basic.CodeFlowTests"; private IdentityServerPipeline _pipeline = new IdentityServerPipeline(); public CodeFlowTests() { _pipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _pipeline.Clients.Add(new Client { Enabled = true, ClientId = "code_pipeline.Client", ClientSecrets = new List { new Secret("secret".Sha512()) }, AllowedGrantTypes = GrantTypes.Code, AllowedScopes = { "openid" }, RequireConsent = false, RequirePkce = false, RedirectUris = new List { "https://code_pipeline.Client/callback", "https://code_pipeline.Client/callback?foo=bar&baz=quux" } }); _pipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _pipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task No_state_should_not_result_in_shash() { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); _pipeline.BrowserClient.AllowAutoRedirect = false; var url = _pipeline.CreateAuthorizeUrl( clientId: "code_pipeline.Client", responseType: "code", scope: "openid", redirectUri: "https://code_pipeline.Client/callback?foo=bar&baz=quux", nonce: nonce); var response = await _pipeline.BrowserClient.GetAsync(url); var authorization = _pipeline.ParseAuthorizationResponseUrl(response.Headers.Location.ToString()); authorization.Code.Should().NotBeNull(); var code = authorization.Code; // backchannel client var wrapper = new MessageHandlerWrapper(_pipeline.Handler); var tokenClient = new HttpClient(wrapper); var tokenResult = await tokenClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = "code_pipeline.Client", ClientSecret = "secret", Code = code, RedirectUri = "https://code_pipeline.Client/callback?foo=bar&baz=quux" }); tokenResult.IsError.Should().BeFalse(); tokenResult.HttpErrorReason.Should().Be("OK"); tokenResult.TokenType.Should().Be("Bearer"); tokenResult.AccessToken.Should().NotBeNull(); tokenResult.ExpiresIn.Should().BeGreaterThan(0); tokenResult.IdentityToken.Should().NotBeNull(); var token = new JwtSecurityToken(tokenResult.IdentityToken); var s_hash = token.Claims.FirstOrDefault(c => c.Type == "s_hash"); s_hash.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task State_should_result_in_shash() { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); _pipeline.BrowserClient.AllowAutoRedirect = false; var url = _pipeline.CreateAuthorizeUrl( clientId: "code_pipeline.Client", responseType: "code", scope: "openid", redirectUri: "https://code_pipeline.Client/callback?foo=bar&baz=quux", state: "state", nonce: nonce); var response = await _pipeline.BrowserClient.GetAsync(url); var authorization = _pipeline.ParseAuthorizationResponseUrl(response.Headers.Location.ToString()); authorization.Code.Should().NotBeNull(); var code = authorization.Code; // backchannel client var wrapper = new MessageHandlerWrapper(_pipeline.Handler); var tokenClient = new HttpClient(wrapper); var tokenResult = await tokenClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = "code_pipeline.Client", ClientSecret = "secret", Code = code, RedirectUri = "https://code_pipeline.Client/callback?foo=bar&baz=quux" }); tokenResult.IsError.Should().BeFalse(); tokenResult.HttpErrorReason.Should().Be("OK"); tokenResult.TokenType.Should().Be("Bearer"); tokenResult.AccessToken.Should().NotBeNull(); tokenResult.ExpiresIn.Should().BeGreaterThan(0); tokenResult.IdentityToken.Should().NotBeNull(); var token = new JwtSecurityToken(tokenResult.IdentityToken); var s_hash = token.Claims.FirstOrDefault(c => c.Type == "s_hash"); s_hash.Should().NotBeNull(); s_hash.Value.Should().Be(CryptoHelper.CreateHashClaimValue("state", "RS256")); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/RedirectUriTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Net; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Conformance.Basic; public class RedirectUriTests { private const string Category = "Conformance.Basic.RedirectUriTests"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public RedirectUriTests() { _mockPipeline.Initialize(); _mockPipeline.Clients.Add(new Client { Enabled = true, ClientId = "code_client", ClientSecrets = new List { new Secret("secret".Sha512()) }, AllowedGrantTypes = GrantTypes.Code, AllowedScopes = { "openid" }, RequireConsent = false, RequirePkce = false, RedirectUris = new List { "https://code_client/callback", "https://code_client/callback?foo=bar&baz=quux" } }); _mockPipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); } [Fact] [Trait("Category", Category)] public async Task Reject_redirect_uri_not_matching_registered_redirect_uri() { await _mockPipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var state = Guid.NewGuid().ToString(); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "code_client", responseType: "code", scope: "openid", redirectUri: "https://bad", state: state, nonce: nonce); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request"); } [Fact] [Trait("Category", Category)] public async Task Reject_request_without_redirect_uri_when_multiple_registered() { await _mockPipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var state = Guid.NewGuid().ToString(); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "code_client", responseType: "code", scope: "openid", // redirectUri deliberately absent redirectUri: null, state: state, nonce: nonce); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request"); } [Fact] [Trait("Category", Category)] public async Task Preserves_query_parameters_in_redirect_uri() { await _mockPipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var state = Guid.NewGuid().ToString(); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "code_client", responseType: "code", scope: "openid", redirectUri: "https://code_client/callback?foo=bar&baz=quux", state: state, nonce: nonce); var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://code_client/callback?"); var authorization = _mockPipeline.ParseAuthorizationResponseUrl(response.Headers.Location.ToString()); authorization.Code.Should().NotBeNull(); authorization.State.Should().Be(state); var query = Microsoft.AspNetCore.WebUtilities.QueryHelpers.ParseQuery(response.Headers.Location.Query); query["foo"].ToString().Should().Be("bar"); query["baz"].ToString().Should().Be("quux"); } [Fact] [Trait("Category", Category)] public async Task Rejects_redirect_uri_when_query_parameter_does_not_match() { await _mockPipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var state = Guid.NewGuid().ToString(); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "code_client", responseType: "code", scope: "openid", redirectUri: "https://code_client/callback?baz=quux&foo=bar", state: state, nonce: nonce); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request"); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Basic/ResponseTypeResponseModeTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Net; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Conformance.Basic; public class ResponseTypeResponseModeTests { private const string Category = "Conformance.Basic.ResponseTypeResponseModeTests"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public ResponseTypeResponseModeTests() { _mockPipeline.Initialize(); _mockPipeline.BrowserClient.AllowAutoRedirect = false; _mockPipeline.Clients.Add(new Client { Enabled = true, ClientId = "code_client", ClientSecrets = new List { new Secret("secret".Sha512()) }, AllowedGrantTypes = GrantTypes.Code, AllowedScopes = { "openid" }, RequireConsent = false, RequirePkce = false, RedirectUris = new List { "https://code_client/callback" } }); _mockPipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); } [Fact] [Trait("Category", Category)] public async Task Request_with_response_type_code_supported() { await _mockPipeline.LoginAsync("bob"); var metadata = await _mockPipeline.BackChannelClient.GetAsync(IdentityServerPipeline.DiscoveryEndpoint); metadata.StatusCode.Should().Be(HttpStatusCode.OK); var state = Guid.NewGuid().ToString(); var nonce = Guid.NewGuid().ToString(); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "code_client", responseType: "code", scope: "openid", redirectUri: "https://code_client/callback", state: state, nonce: nonce); var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Found); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IsError.Should().BeFalse(); authorization.Code.Should().NotBeNull(); authorization.State.Should().Be(state); } // this might not be in sync with the actual conformance tests // since we dead-end on the error page due to changes // to follow the RFC to address open redirect in original OAuth RFC [Fact] [Trait("Category", Category)] public async Task Request_missing_response_type_rejected() { await _mockPipeline.LoginAsync("bob"); var state = Guid.NewGuid().ToString(); var nonce = Guid.NewGuid().ToString(); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "code_client", responseType: null, // missing scope: "openid", redirectUri: "https://code_client/callback", state: state, nonce: nonce); _mockPipeline.BrowserClient.AllowAutoRedirect = true; var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("unsupported_response_type"); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Conformance/Pkce/PkceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityModel.Client; using IdentityServer.IntegrationTests.Common; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Conformance.Pkce; public class PkceTests { private const string Category = "PKCE"; private IdentityServerPipeline _pipeline = new IdentityServerPipeline(); private Client client; private const string client_id = "code_client"; private const string client_id_optional = "code_client_optional"; private const string client_id_plain = "code_plain_client"; private const string client_id_pkce = "codewithproofkey_client"; private const string client_id_pkce_plain = "codewithproofkey_plain_client"; private string redirect_uri = "https://code_client/callback"; private string code_verifier = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; private string client_secret = "secret"; private string response_type = "code"; public PkceTests() { _pipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _pipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _pipeline.Clients.Add(client = new Client { Enabled = true, ClientId = client_id, ClientSecrets = new List { new Secret(client_secret.Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AllowedScopes = { "openid" }, RequireConsent = false, RedirectUris = new List { redirect_uri } }); _pipeline.Clients.Add(client = new Client { Enabled = true, ClientId = client_id_optional, ClientSecrets = new List { new Secret(client_secret.Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = false, AllowedScopes = { "openid" }, RequireConsent = false, RedirectUris = new List { redirect_uri } }); _pipeline.Clients.Add(client = new Client { Enabled = true, ClientId = client_id_pkce, ClientSecrets = new List { new Secret(client_secret.Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AllowedScopes = { "openid" }, RequireConsent = false, RedirectUris = new List { redirect_uri } }); // allow plain text PKCE _pipeline.Clients.Add(client = new Client { Enabled = true, ClientId = client_id_plain, ClientSecrets = new List { new Secret(client_secret.Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AllowPlainTextPkce = true, AllowedScopes = { "openid" }, RequireConsent = false, RedirectUris = new List { redirect_uri } }); _pipeline.Clients.Add(client = new Client { Enabled = true, ClientId = client_id_pkce_plain, ClientSecrets = new List { new Secret(client_secret.Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AllowPlainTextPkce = true, AllowedScopes = { "openid" }, RequireConsent = false, RedirectUris = new List { redirect_uri } }); _pipeline.Initialize(); } [Theory] [InlineData(client_id)] [InlineData(client_id_pkce)] [Trait("Category", Category)] public async Task Client_cannot_use_plain_code_challenge_method(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: code_challenge, codeChallengeMethod: OidcConstants.CodeChallengeMethods.Plain); _pipeline.ErrorWasCalled.Should().BeTrue(); _pipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Theory] [InlineData(client_id_plain)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Client_can_use_plain_code_challenge_method(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: code_challenge, codeChallengeMethod: OidcConstants.CodeChallengeMethods.Plain); authorizeResponse.IsError.Should().BeFalse(); var code = authorizeResponse.Code; var tokenResponse = await _pipeline.BackChannelClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = clientId, ClientSecret = client_secret, Code = code, RedirectUri = redirect_uri, CodeVerifier = code_verifier }); tokenResponse.IsError.Should().BeFalse(); tokenResponse.TokenType.Should().Be("Bearer"); tokenResponse.AccessToken.Should().NotBeNull(); tokenResponse.IdentityToken.Should().NotBeNull(); tokenResponse.ExpiresIn.Should().BeGreaterThan(0); } [Theory] [InlineData(client_id)] [InlineData(client_id_pkce)] [Trait("Category", Category)] public async Task Client_can_use_sha256_code_challenge_method(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = Sha256OfCodeVerifier(code_verifier); var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: code_challenge, codeChallengeMethod: OidcConstants.CodeChallengeMethods.Sha256); authorizeResponse.IsError.Should().BeFalse(); var code = authorizeResponse.Code; var tokenResponse = await _pipeline.BackChannelClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = clientId, ClientSecret = client_secret, Code = code, RedirectUri = redirect_uri, CodeVerifier = code_verifier }); tokenResponse.IsError.Should().BeFalse(); tokenResponse.TokenType.Should().Be("Bearer"); tokenResponse.AccessToken.Should().NotBeNull(); tokenResponse.IdentityToken.Should().NotBeNull(); tokenResponse.ExpiresIn.Should().BeGreaterThan(0); } [Theory] [InlineData(client_id_pkce)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Authorize_request_needs_code_challenge(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce); authorizeResponse.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task Code_verifier_should_not_be_accepted_if_no_code_challenge_was_used() { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(client_id_optional, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce); authorizeResponse.IsError.Should().BeFalse(); var code = authorizeResponse.Code; var tokenResponse = await _pipeline.BackChannelClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = client_id_optional, ClientSecret = client_secret, Code = code, RedirectUri = redirect_uri, CodeVerifier = code_verifier }); tokenResponse.IsError.Should().BeTrue(); } [Theory] [InlineData(client_id)] [InlineData(client_id_plain)] [InlineData(client_id_pkce)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Authorize_request_code_challenge_cannot_be_too_short(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge:"a"); _pipeline.ErrorWasCalled.Should().BeTrue(); _pipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Theory] [InlineData(client_id)] [InlineData(client_id_plain)] [InlineData(client_id_pkce)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Authorize_request_code_challenge_cannot_be_too_long(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: new string('a', _pipeline.Options.InputLengthRestrictions.CodeChallengeMaxLength + 1) ); _pipeline.ErrorWasCalled.Should().BeTrue(); _pipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Theory] [InlineData(client_id)] [InlineData(client_id_plain)] [InlineData(client_id_pkce)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Authorize_request_needs_supported_code_challenge_method(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: code_challenge, codeChallengeMethod: "unknown_code_challenge_method" ); authorizeResponse.Should().BeNull(); } [Theory] [InlineData(client_id_plain)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Token_request_needs_code_verifier(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: code_challenge, codeChallengeMethod: OidcConstants.CodeChallengeMethods.Plain); authorizeResponse.IsError.Should().BeFalse(); var code = authorizeResponse.Code; var tokenResponse = await _pipeline.BackChannelClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = clientId, ClientSecret = client_secret, Code = code, RedirectUri = redirect_uri, }); tokenResponse.IsError.Should().BeTrue(); tokenResponse.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Theory] [InlineData(client_id_plain)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Token_request_code_verifier_cannot_be_too_short(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: code_challenge, codeChallengeMethod: OidcConstants.CodeChallengeMethods.Plain); authorizeResponse.IsError.Should().BeFalse(); var code = authorizeResponse.Code; var tokenResponse = await _pipeline.BackChannelClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = clientId, ClientSecret = client_secret, Code = code, RedirectUri = redirect_uri, CodeVerifier = "a" }); tokenResponse.IsError.Should().BeTrue(); tokenResponse.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Theory] [InlineData(client_id_plain)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Token_request_code_verifier_cannot_be_too_long(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: code_challenge, codeChallengeMethod: OidcConstants.CodeChallengeMethods.Plain); authorizeResponse.IsError.Should().BeFalse(); var code = authorizeResponse.Code; var tokenResponse = await _pipeline.BackChannelClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = clientId, ClientSecret = client_secret, Code = code, RedirectUri = redirect_uri, CodeVerifier = new string('a', _pipeline.Options.InputLengthRestrictions.CodeVerifierMaxLength + 1) }); tokenResponse.IsError.Should().BeTrue(); tokenResponse.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Theory] [InlineData(client_id_plain)] [InlineData(client_id_pkce_plain)] [Trait("Category", Category)] public async Task Token_request_code_verifier_must_match_with_code_chalenge(string clientId) { await _pipeline.LoginAsync("bob"); var nonce = Guid.NewGuid().ToString(); var code_challenge = code_verifier; var authorizeResponse = await _pipeline.RequestAuthorizationEndpointAsync(clientId, response_type, IdentityServerConstants.StandardScopes.OpenId, redirect_uri, nonce: nonce, codeChallenge: code_challenge, codeChallengeMethod: OidcConstants.CodeChallengeMethods.Plain); authorizeResponse.IsError.Should().BeFalse(); var code = authorizeResponse.Code; var tokenResponse = await _pipeline.BackChannelClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = clientId, ClientSecret = client_secret, Code = code, RedirectUri = redirect_uri, CodeVerifier = "mismatched_code_verifier" }); tokenResponse.IsError.Should().BeTrue(); tokenResponse.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } private static string Sha256OfCodeVerifier(string codeVerifier) { var codeVerifierBytes = Encoding.ASCII.GetBytes(codeVerifier); var hashedBytes = codeVerifierBytes.Sha256(); var transformedCodeVerifier = Base64Url.Encode(hashedBytes); return transformedCodeVerifier; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/AuthorizeTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.IntegrationTests.Common; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Stores.Default; using IdentityServer8.Test; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.Authorize; public class AuthorizeTests { private const string Category = "Authorize endpoint"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); private Client _client1; public AuthorizeTests() { _mockPipeline.Clients.AddRange(new Client[] { _client1 = new Client { ClientId = "client1", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid", "profile" }, RedirectUris = new List { "https://client1/callback" }, AllowAccessTokensViaBrowser = true }, new Client { ClientId = "client2", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = true, AllowedScopes = new List { "openid", "profile", "api1", "api2" }, RedirectUris = new List { "https://client2/callback" }, AllowAccessTokensViaBrowser = true }, new Client { ClientId = "client3", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid", "profile", "api1", "api2" }, RedirectUris = new List { "https://client3/callback" }, AllowAccessTokensViaBrowser = true, EnableLocalLogin = false, IdentityProviderRestrictions = new List { "google" } }, new Client { ClientId = "client4", AllowedGrantTypes = GrantTypes.Code, RequireClientSecret = false, RequireConsent = false, RequirePkce = false, AllowedScopes = new List { "openid", "profile", "api1", "api2" }, RedirectUris = new List { "https://client4/callback" }, }, }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email() }); _mockPipeline.ApiResources.AddRange(new ApiResource[] { new ApiResource { Name = "api", Scopes = { "api1", "api2" } } }); _mockPipeline.ApiScopes.AddRange(new ApiScope[] { new ApiScope { Name = "api1" }, new ApiScope { Name = "api2" } }); _mockPipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task get_request_should_not_return_404() { var response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.AuthorizeEndpoint); response.StatusCode.Should().NotBe(HttpStatusCode.NotFound); } [Fact] [Trait("Category", Category)] public async Task post_request_without_form_should_return_415() { var response = await _mockPipeline.BrowserClient.PostAsync(IdentityServerPipeline.AuthorizeEndpoint, new StringContent("foo")); response.StatusCode.Should().Be(HttpStatusCode.UnsupportedMediaType); } [Fact] [Trait("Category", Category)] public async Task post_request_should_return_200() { var response = await _mockPipeline.BrowserClient.PostAsync(IdentityServerPipeline.AuthorizeEndpoint, new FormUrlEncodedContent( new Dictionary { })); response.StatusCode.Should().Be(HttpStatusCode.OK); } [Fact] [Trait("Category", Category)] public async Task get_request_should_not_return_500() { var response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.AuthorizeEndpoint); ((int) response.StatusCode).Should().BeLessThan(500); } [Fact] [Trait("Category", Category)] public async Task anonymous_user_should_be_redirected_to_login_page() { var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginWasCalled.Should().BeTrue(); } [Theory] [InlineData((Type) null)] [InlineData(typeof(QueryStringAuthorizationParametersMessageStore))] [InlineData(typeof(DistributedCacheAuthorizationParametersMessageStore))] [Trait("Category", Category)] public async Task signin_request_should_have_authorization_params(Type storeType) { if (storeType != null) { _mockPipeline.OnPostConfigureServices += services => { services.AddTransient(typeof(IAuthorizationParametersMessageStore), storeType); }; _mockPipeline.Initialize(); } var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce", loginHint: "login_hint_value", acrValues: "acr_1 acr_2 tenant:tenant_value idp:idp_value", extra: new { display = "popup", // must use a valid value from the spec for display ui_locales = "ui_locale_value", custom_foo = "foo_value" }); var response = await _mockPipeline.BrowserClient.GetAsync(url + "&foo=bar"); _mockPipeline.LoginRequest.Should().NotBeNull(); _mockPipeline.LoginRequest.Client.ClientId.Should().Be("client1"); _mockPipeline.LoginRequest.DisplayMode.Should().Be("popup"); _mockPipeline.LoginRequest.UiLocales.Should().Be("ui_locale_value"); _mockPipeline.LoginRequest.IdP.Should().Be("idp_value"); _mockPipeline.LoginRequest.Tenant.Should().Be("tenant_value"); _mockPipeline.LoginRequest.LoginHint.Should().Be("login_hint_value"); _mockPipeline.LoginRequest.AcrValues.Should().BeEquivalentTo(new string[] { "acr_2", "acr_1" }); _mockPipeline.LoginRequest.Parameters.AllKeys.Should().Contain("foo"); _mockPipeline.LoginRequest.Parameters["foo"].Should().Be("bar"); } [Fact] [Trait("Category", Category)] public async Task signin_response_should_allow_successful_authorization_response() { _mockPipeline.Subject = new IdentityServerUser("bob").CreatePrincipal(); _mockPipeline.BrowserClient.StopRedirectingAfter = 2; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://client1/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IsError.Should().BeFalse(); authorization.IdentityToken.Should().NotBeNull(); authorization.State.Should().Be("123_state"); } [Fact] [Trait("Category", Category)] public async Task authenticated_user_with_valid_request_should_receive_authorization_response() { await _mockPipeline.LoginAsync("bob"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://client1/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IsError.Should().BeFalse(); authorization.IdentityToken.Should().NotBeNull(); authorization.State.Should().Be("123_state"); } [Theory] [InlineData((Type) null)] [InlineData(typeof(QueryStringAuthorizationParametersMessageStore))] [InlineData(typeof(DistributedCacheAuthorizationParametersMessageStore))] [Trait("Category", Category)] public async Task login_response_and_consent_response_should_receive_authorization_response(Type storeType) { if (storeType != null) { _mockPipeline.OnPostConfigureServices += services => { services.AddTransient(typeof(IAuthorizationParametersMessageStore), storeType); }; _mockPipeline.Initialize(); } _mockPipeline.Subject = new IdentityServerUser("bob").CreatePrincipal(); _mockPipeline.ConsentResponse = new ConsentResponse() { ScopesValuesConsented = new string[] { "openid", "api1", "profile" } }; _mockPipeline.BrowserClient.StopRedirectingAfter = 4; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token token", scope: "openid profile api1 api2", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://client2/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IsError.Should().BeFalse(); authorization.IdentityToken.Should().NotBeNull(); authorization.State.Should().Be("123_state"); var scopes = authorization.Scope.Split(' '); scopes.Should().BeEquivalentTo(new string[] { "profile", "api1", "openid" }); } [Fact] [Trait("Category", Category)] public async Task idp_should_be_passed_to_login_page() { var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client3", responseType: "id_token", scope: "openid profile", redirectUri: "https://client3/callback", state: "123_state", nonce: "123_nonce", acrValues: "idp:google"); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginWasCalled.Should().BeTrue(); _mockPipeline.LoginRequest.IdP.Should().Be("google"); } [Fact] [Trait("Category", Category)] public async Task idp_not_allowed_by_client_should_not_be_passed_to_login_page() { var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client3", responseType: "id_token", scope: "openid profile", redirectUri: "https://client3/callback", state: "123_state", nonce: "123_nonce", acrValues: "idp:facebook"); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginWasCalled.Should().BeTrue(); _mockPipeline.LoginRequest.IdP.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task user_idp_not_allowed_by_client_should_cause_login_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client3", responseType: "id_token", scope: "openid profile", redirectUri: "https://client3/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginWasCalled.Should().BeTrue(); _mockPipeline.LoginRequest.IdP.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task for_invalid_client_error_page_should_not_receive_client_id() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: null, responseType: "id_token", scope: "openid", redirectUri: "https://invalid", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.ClientId.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task error_page_should_receive_client_id() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://invalid", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.ClientId.Should().Be("client1"); } [Fact] [Trait("Category", Category)] public async Task invalid_redirect_uri_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://invalid", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("redirect_uri"); } [Fact] [Trait("Category", Category)] public async Task invalid_redirect_uri_should_not_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://invalid", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().BeNull(); _mockPipeline.ErrorMessage.ResponseMode.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task invalid_client_id_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1_invalid", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task invalid_client_id_should_not_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1_invalid", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().BeNull(); _mockPipeline.ErrorMessage.ResponseMode.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task missing_redirect_uri_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1_invalid", responseType: "id_token", scope: "openid", //redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("client"); } [Fact] [Trait("Category", Category)] public async Task missing_redirect_uri_should_not_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1_invalid", responseType: "id_token", scope: "openid", //redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().BeNull(); _mockPipeline.ErrorMessage.ResponseMode.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task malformed_redirect_uri_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1_invalid", responseType: "id_token", scope: "openid", redirectUri: "invalid-uri", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("client"); } [Fact] [Trait("Category", Category)] public async Task disabled_client_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); _client1.Enabled = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task disabled_client_should_not_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); _client1.Enabled = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().BeNull(); _mockPipeline.ErrorMessage.ResponseMode.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task invalid_protocol_for_client_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); _client1.ProtocolType = "invalid"; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("protocol"); } [Fact] [Trait("Category", Category)] public async Task invalid_protocol_for_client_should_not_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); _client1.ProtocolType = "invalid"; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().BeNull(); _mockPipeline.ErrorMessage.ResponseMode.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task invalid_response_type_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "invalid", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.UnsupportedResponseType); } [Fact] [Trait("Category", Category)] public async Task invalid_response_type_should_not_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "invalid", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().BeNull(); _mockPipeline.ErrorMessage.ResponseMode.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task invalid_response_mode_for_flow_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", responseMode: "query", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("response_mode"); } [Fact] [Trait("Category", Category)] public async Task invalid_response_mode_for_flow_should_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", responseMode: "query", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task invalid_response_mode_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", responseMode: "invalid", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.UnsupportedResponseType); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("response_mode"); } [Fact] [Trait("Category", Category)] public async Task invalid_response_mode_should_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", responseMode: "invalid", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task missing_scope_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", //scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("scope"); } [Fact] [Trait("Category", Category)] public async Task missing_scope_should_pass_return_url_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", //scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task explicit_response_mode_should_be_passed_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", responseMode: "form_post", //scope: "openid", // this will cause the error redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("form_post"); } [Fact] [Trait("Category", Category)] public async Task scope_too_long_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: new string('x', 500), redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("scope"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task missing_openid_scope_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "profile", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("scope"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("openid"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task client_not_allowed_access_to_scope_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid email", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("scope"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task missing_nonce_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state" //nonce: "123_nonce" ); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("nonce"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task nonce_too_long_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: new string('x', 500)); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("nonce"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task locale_too_long_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce", extra: new { ui_locales = new string('x', 500) }); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("ui_locales"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task invalid_max_age_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce", extra: new { max_age = "invalid" }); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("max_age"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task negative_max_age_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce", extra: new { max_age = "-10" }); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("max_age"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task login_hint_too_long_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce", loginHint: new string('x', 500)); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("login_hint"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task acr_values_too_long_should_show_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce", acrValues: new string('x', 500)); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); _mockPipeline.ErrorMessage.ErrorDescription.Should().Contain("acr_values"); _mockPipeline.ErrorMessage.RedirectUri.Should().StartWith("https://client1/callback"); _mockPipeline.ErrorMessage.ResponseMode.Should().Be("fragment"); } [Fact] [Trait("Category", Category)] public async Task overlapping_identity_scopes_and_api_scopes_should_show_error_page() { _mockPipeline.IdentityScopes.Add(new IdentityResource("foo", "Foo", new string[] { "name" })); _mockPipeline.IdentityScopes.Add(new IdentityResource("bar", "Bar", new string[] { "name" })); _mockPipeline.ApiScopes.Add(new ApiScope("foo", "Foo")); _mockPipeline.ApiScopes.Add(new ApiScope("bar", "Bar")); await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid foo bar", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); Func a = async () => await _mockPipeline.BrowserClient.GetAsync(url); await a.Should().ThrowAsync(); } [Fact] [Trait("Category", Category)] public async Task ui_locales_should_be_passed_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce", acrValues: new string('x', 500), extra: new { ui_locales = "fr-FR" }); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.UiLocales.Should().Be("fr-FR"); } [Fact] [Trait("Category", Category)] public async Task display_mode_should_be_passed_to_error_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce", acrValues: new string('x', 500), extra: new { display = "popup" }); await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.ErrorMessage.DisplayMode.Should().Be("popup"); } [Fact] [Trait("Category", Category)] public async Task unicode_values_in_url_should_be_processed_correctly() { var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); url = url.Replace(IdentityServerPipeline.BaseUrl, "https://грант.рф"); var result = await _mockPipeline.BackChannelClient.GetAsync(url); result.Headers.Location.Authority.Should().Be("xn--80af5akm.xn--p1ai"); } [Fact] [Trait("Category", Category)] public async Task code_flow_with_fragment_response_type_should_be_allowed() { var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client4", responseType: "code", responseMode: "fragment", scope: "openid", redirectUri: "https://client4/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task prompt_login_should_show_login_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client3", responseType: "id_token", scope: "openid profile", redirectUri: "https://client3/callback", state: "123_state", nonce: "123_nonce", extra: new { prompt = "login" } ); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginWasCalled.Should().BeTrue(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/ConsentTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Net; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Stores.Default; using IdentityServer8.Test; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.Authorize; public class ConsentTests { private const string Category = "Authorize and consent tests"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public ConsentTests() { _mockPipeline.Clients.AddRange(new Client[] { new Client { ClientId = "client1", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid", "profile" }, RedirectUris = new List { "https://client1/callback" }, AllowAccessTokensViaBrowser = true }, new Client { ClientId = "client2", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = true, AllowedScopes = new List { "openid", "profile", "api1", "api2" }, RedirectUris = new List { "https://client2/callback" }, AllowAccessTokensViaBrowser = true }, new Client { ClientId = "client3", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid", "profile", "api1", "api2" }, RedirectUris = new List { "https://client3/callback" }, AllowAccessTokensViaBrowser = true, IdentityProviderRestrictions = new List { "google" } } }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email() }); _mockPipeline.ApiResources.AddRange(new ApiResource[] { new ApiResource { Name = "api", Scopes = { "api1", "api2" } } }); _mockPipeline.ApiScopes.AddRange(new ApiScope[] { new ApiScope { Name = "api1" }, new ApiScope { Name = "api2" } }); _mockPipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task client_requires_consent_should_show_consent_page() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token", scope: "openid", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce" ); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ConsentWasCalled.Should().BeTrue(); } [Theory] [InlineData((Type)null)] [InlineData(typeof(QueryStringAuthorizationParametersMessageStore))] [InlineData(typeof(DistributedCacheAuthorizationParametersMessageStore))] [Trait("Category", Category)] public async Task consent_page_should_have_authorization_params(Type storeType) { if (storeType != null) { _mockPipeline.OnPostConfigureServices += services => { services.AddTransient(typeof(IAuthorizationParametersMessageStore), storeType); }; _mockPipeline.Initialize(); } await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token token", scope: "openid api1 api2", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce", acrValues: "acr_1 acr_2 tenant:tenant_value", extra: new { display = "popup", // must use a valid value form the spec for display ui_locales = "ui_locale_value", custom_foo = "foo_value" } ); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ConsentRequest.Should().NotBeNull(); _mockPipeline.ConsentRequest.Client.ClientId.Should().Be("client2"); _mockPipeline.ConsentRequest.DisplayMode.Should().Be("popup"); _mockPipeline.ConsentRequest.UiLocales.Should().Be("ui_locale_value"); _mockPipeline.ConsentRequest.Tenant.Should().Be("tenant_value"); _mockPipeline.ConsentRequest.AcrValues.Should().BeEquivalentTo(new string[] { "acr_2", "acr_1" }); _mockPipeline.ConsentRequest.Parameters.AllKeys.Should().Contain("custom_foo"); _mockPipeline.ConsentRequest.Parameters["custom_foo"].Should().Be("foo_value"); _mockPipeline.ConsentRequest.ValidatedResources.RawScopeValues.Should().BeEquivalentTo(new string[] { "api2", "openid", "api1" }); } [Theory] [InlineData((Type)null)] [InlineData(typeof(QueryStringAuthorizationParametersMessageStore))] [InlineData(typeof(DistributedCacheAuthorizationParametersMessageStore))] [Trait("Category", Category)] public async Task consent_response_should_allow_successful_authorization_response(Type storeType) { if (storeType != null) { _mockPipeline.OnPostConfigureServices += services => { services.AddTransient(typeof(IAuthorizationParametersMessageStore), storeType); }; _mockPipeline.Initialize(); } await _mockPipeline.LoginAsync("bob"); _mockPipeline.ConsentResponse = new ConsentResponse() { ScopesValuesConsented = new string[] { "openid", "api2" } }; _mockPipeline.BrowserClient.StopRedirectingAfter = 2; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token token", scope: "openid profile api1 api2", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://client2/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IsError.Should().BeFalse(); authorization.IdentityToken.Should().NotBeNull(); authorization.State.Should().Be("123_state"); var scopes = authorization.Scope.Split(' '); scopes.Should().BeEquivalentTo(new string[] { "api2", "openid" }); } [Fact] [Trait("Category", Category)] public async Task consent_response_should_reject_modified_request_params() { await _mockPipeline.LoginAsync("bob"); _mockPipeline.ConsentResponse = new ConsentResponse() { ScopesValuesConsented = new string[] { "openid", "api2" } }; _mockPipeline.BrowserClient.AllowAutoRedirect = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token token", scope: "openid profile api2", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://server/consent"); response = await _mockPipeline.BrowserClient.GetAsync(response.Headers.Location.ToString()); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("/connect/authorize/callback"); var modifiedAuthorizeCallback = "https://server" + response.Headers.Location.ToString(); modifiedAuthorizeCallback = modifiedAuthorizeCallback.Replace("api2", "api1%20api2"); response = await _mockPipeline.BrowserClient.GetAsync(modifiedAuthorizeCallback); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://server/consent"); } [Fact()] [Trait("Category", Category)] public async Task consent_response_missing_required_scopes_should_error() { await _mockPipeline.LoginAsync("bob"); _mockPipeline.ConsentResponse = new ConsentResponse() { ScopesValuesConsented = new string[] { "api2" } }; _mockPipeline.BrowserClient.StopRedirectingAfter = 2; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token token", scope: "openid profile api1 api2", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://client2/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IsError.Should().BeTrue(); authorization.Error.Should().Be("access_denied"); authorization.State.Should().Be("123_state"); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/JwtRequestAuthorizeTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Net.Http; using System.Net.Http.Headers; using System.Security.Claims; using System.Security.Cryptography.X509Certificates; using System.Text.Json; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.IntegrationTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Test; using Microsoft.IdentityModel.Logging; using Microsoft.IdentityModel.Tokens; using Xunit; using JsonWebKey = Microsoft.IdentityModel.Tokens.JsonWebKey; namespace IdentityServer.IntegrationTests.Endpoints.Authorize; public class JwtRequestAuthorizeTests { private const string Category = "Authorize endpoint with JWT requests"; private readonly IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); private readonly Client _client; private readonly string _symmetricJwk = @"{ ""kty"": ""oct"", ""use"": ""sig"", ""kid"": ""1"", ""k"": ""nYA-IFt8xTsdBHe9hunvizcp3Dt7f6qGqudq18kZHNtvqEGjJ9Ud-9x3kbQ-LYfLHS3xM2MpFQFg1JzT_0U_F8DI40oby4TvBDGszP664UgA8_5GjB7Flnrlsap1NlitvNpgQX3lpyTvC2zVuQ-UVsXbBDAaSBUSlnw7SE4LM8Ye2WYZrdCCXL8yAX9vIR7vf77yvNTEcBCI6y4JlvZaqMB4YKVSfygs8XqGGCHjLpE5bvI-A4ESbAUX26cVFvCeDg9pR6HK7BmwPMlO96krgtKZcXEJtUELYPys6-rbwAIdmxJxKxpgRpt0FRv_9fm6YPwG7QivYBX-vRwaodL1TA"", ""alg"": ""HS256""}"; private readonly RsaSecurityKey _rsaKey; public JwtRequestAuthorizeTests() { IdentityModelEventSource.ShowPII = true; _rsaKey = CryptoHelper.CreateRsaSecurityKey(); _mockPipeline.Clients.AddRange(new Client[] { _client = new Client { ClientName = "Client with keys", ClientId = "client", Enabled = true, RequireRequestObject = true, RedirectUris = { "https://client/callback" }, ClientSecrets = { new Secret { // x509 cert as base64 string Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = Convert.ToBase64String(TestCert.Load().Export(X509ContentType.Cert)) }, new Secret { // symmetric key as JWK Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = _symmetricJwk }, new Secret { // RSA key as JWK Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = JsonSerializer.Serialize(JsonWebKeyConverter.ConvertFromRSASecurityKey(_rsaKey)) }, new Secret { // x509 cert as JWK Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = JsonSerializer.Serialize(JsonWebKeyConverter.ConvertFromX509SecurityKey(new X509SecurityKey(TestCert.Load()))) } }, AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = new List { "openid", "profile", "api1", "api2" } }, _client = new Client { ClientName = "Client with keys", ClientId = "client2", Enabled = true, RequireRequestObject = true, RedirectUris = { "https://client/callback" }, ClientSecrets = { new Secret { // x509 cert as base64 string Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = Convert.ToBase64String(TestCert.Load().Export(X509ContentType.Cert)) }, new Secret { // symmetric key as JWK Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = _symmetricJwk }, new Secret { // RSA key as JWK Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = JsonSerializer.Serialize(JsonWebKeyConverter.ConvertFromRSASecurityKey(_rsaKey)) }, new Secret { // x509 cert as JWK Type = IdentityServerConstants.SecretTypes.JsonWebKey, Value = JsonSerializer.Serialize(JsonWebKeyConverter.ConvertFromX509SecurityKey(new X509SecurityKey(TestCert.Load()))) } }, AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = new List { "openid", "profile", "api1", "api2" } }, }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email() }); _mockPipeline.ApiResources.AddRange(new ApiResource[] { new ApiResource { Name = "api", Scopes = { "api1", "api2" } } }); _mockPipeline.ApiScopes.AddRange(new ApiScope[] { new ApiScope { Name = "api1" }, new ApiScope { Name = "api2" } }); _mockPipeline.Initialize(); } string CreateRequestJwt(string issuer, string audience, SigningCredentials credential, Claim[] claims, bool setJwtTyp = false) { var handler = new JwtSecurityTokenHandler(); handler.OutboundClaimTypeMap.Clear(); var token = handler.CreateJwtSecurityToken( issuer: issuer, audience: audience, signingCredentials: credential, subject: Identity.Create("pwd", claims)); if (setJwtTyp) { token.Header["typ"] = JwtClaimTypes.JwtTypes.AuthorizationRequest; } return handler.WriteToken(token); } [Fact] [Trait("Category", Category)] public async Task missing_request_object_should_fail() { var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", scope: "openid profile", state: "123state", nonce: "123nonce", redirectUri: "https://client/callback"); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Client must use request object, but no request or request_uri parameter present"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_accept_valid_JWT_request_object_parameters_using_X509_certificate() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginRequest.Should().NotBeNull(); _mockPipeline.LoginRequest.Client.ClientId.Should().Be(_client.ClientId); _mockPipeline.LoginRequest.DisplayMode.Should().Be("popup"); _mockPipeline.LoginRequest.UiLocales.Should().Be("ui_locale_value"); _mockPipeline.LoginRequest.IdP.Should().Be("idp_value"); _mockPipeline.LoginRequest.Tenant.Should().Be("tenant_value"); _mockPipeline.LoginRequest.LoginHint.Should().Be("login_hint_value"); _mockPipeline.LoginRequest.AcrValues.Should().BeEquivalentTo(new string[] { "acr_2", "acr_1" }); _mockPipeline.LoginRequest.Parameters.AllKeys.Should().Contain("foo"); _mockPipeline.LoginRequest.Parameters["foo"].Should().Be("123foo"); _mockPipeline.LoginRequest.RequestObjectValues.Count.Should().Be(11); _mockPipeline.LoginRequest.RequestObjectValues.Should().ContainKey("foo"); _mockPipeline.LoginRequest.RequestObjectValues["foo"].Should().Be("123foo"); } [Fact] [Trait("Category", Category)] public async Task authorize_should_accept_valid_JWT_request_object_parameters_using_symmetric_jwk() { var jwk = new Microsoft.IdentityModel.Tokens.JsonWebKey(_symmetricJwk); var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new SigningCredentials(jwk, "HS256"), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginRequest.Should().NotBeNull(); _mockPipeline.LoginRequest.Client.ClientId.Should().Be(_client.ClientId); _mockPipeline.LoginRequest.DisplayMode.Should().Be("popup"); _mockPipeline.LoginRequest.UiLocales.Should().Be("ui_locale_value"); _mockPipeline.LoginRequest.IdP.Should().Be("idp_value"); _mockPipeline.LoginRequest.Tenant.Should().Be("tenant_value"); _mockPipeline.LoginRequest.LoginHint.Should().Be("login_hint_value"); _mockPipeline.LoginRequest.AcrValues.Should().BeEquivalentTo(new string[] { "acr_2", "acr_1" }); _mockPipeline.LoginRequest.Parameters.AllKeys.Should().Contain("foo"); _mockPipeline.LoginRequest.Parameters["foo"].Should().Be("123foo"); _mockPipeline.LoginRequest.RequestObjectValues.Count.Should().Be(11); _mockPipeline.LoginRequest.RequestObjectValues.Should().ContainKey("foo"); _mockPipeline.LoginRequest.RequestObjectValues["foo"].Should().Be("123foo"); } [Fact] [Trait("Category", Category)] public async Task authorize_should_accept_valid_JWT_request_object_parameters_using_rsa_jwk() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new SigningCredentials(_rsaKey, "RS256"), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginRequest.Should().NotBeNull(); _mockPipeline.LoginRequest.Client.ClientId.Should().Be(_client.ClientId); _mockPipeline.LoginRequest.DisplayMode.Should().Be("popup"); _mockPipeline.LoginRequest.UiLocales.Should().Be("ui_locale_value"); _mockPipeline.LoginRequest.IdP.Should().Be("idp_value"); _mockPipeline.LoginRequest.Tenant.Should().Be("tenant_value"); _mockPipeline.LoginRequest.LoginHint.Should().Be("login_hint_value"); _mockPipeline.LoginRequest.AcrValues.Should().BeEquivalentTo(new string[] { "acr_2", "acr_1" }); _mockPipeline.LoginRequest.Parameters.AllKeys.Should().Contain("foo"); _mockPipeline.LoginRequest.Parameters["foo"].Should().Be("123foo"); _mockPipeline.LoginRequest.RequestObjectValues.Count.Should().Be(11); _mockPipeline.LoginRequest.RequestObjectValues.Should().ContainKey("foo"); _mockPipeline.LoginRequest.RequestObjectValues["foo"].Should().Be("123foo"); } [Fact] [Trait("Category", Category)] public async Task correct_jwt_typ_should_pass_strict_validation() { _mockPipeline.Options.StrictJarValidation = true; var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new SigningCredentials(_rsaKey, "RS256"), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }, setJwtTyp: true); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginRequest.Should().NotBeNull(); _mockPipeline.LoginRequest.Client.ClientId.Should().Be(_client.ClientId); _mockPipeline.LoginRequest.DisplayMode.Should().Be("popup"); _mockPipeline.LoginRequest.UiLocales.Should().Be("ui_locale_value"); _mockPipeline.LoginRequest.IdP.Should().Be("idp_value"); _mockPipeline.LoginRequest.Tenant.Should().Be("tenant_value"); _mockPipeline.LoginRequest.LoginHint.Should().Be("login_hint_value"); _mockPipeline.LoginRequest.AcrValues.Should().BeEquivalentTo(new string[] { "acr_2", "acr_1" }); _mockPipeline.LoginRequest.Parameters.AllKeys.Should().Contain("foo"); _mockPipeline.LoginRequest.Parameters["foo"].Should().Be("123foo"); _mockPipeline.LoginRequest.RequestObjectValues.Count.Should().Be(11); _mockPipeline.LoginRequest.RequestObjectValues.Should().ContainKey("foo"); _mockPipeline.LoginRequest.RequestObjectValues["foo"].Should().Be("123foo"); } [Fact] [Trait("Category", Category)] public async Task missing_jwt_typ_should_error() { _mockPipeline.Options.StrictJarValidation = true; var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new SigningCredentials(_rsaKey, "RS256"), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request_object"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task mismatch_in_jwt_values_should_error() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new SigningCredentials(_rsaKey, "RS256"), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", scope: "bad", state: "bad", nonce: "bad", redirectUri: "bad", acrValues: "bad", loginHint: "bad", extra: new { display = "bad", ui_locales = "bad", foo = "bad", request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Parameter mismatch in JWT request"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_accept_complex_objects_in_request_object() { var someObj = new { foo = new { bar = "bar" }, baz = "baz" }; var someObjJson = JsonSerializer.Serialize(someObj); var someArr = new[] { "a", "b", "c" }; var someArrJson = JsonSerializer.Serialize(someArr); var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), new Claim("someObj", someObjJson), new Claim("someArr", someArrJson), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginRequest.Should().NotBeNull(); _mockPipeline.LoginRequest.Parameters["someObj"].Should().NotBeNull(); var someObj2 = JsonSerializer.Deserialize(_mockPipeline.LoginRequest.Parameters["someObj"], someObj.GetType()); someObj.Should().BeEquivalentTo(someObj2); _mockPipeline.LoginRequest.Parameters["someArr"].Should().NotBeNull(); var someArr2 =JsonSerializer.Deserialize(_mockPipeline.LoginRequest.Parameters["someArr"]); someArr2.Should().Contain(new[] { "a", "c", "b" }); someArr2.Length.Should().Be(3); _mockPipeline.LoginRequest.RequestObjectValues.Count.Should().Be(13); _mockPipeline.LoginRequest.RequestObjectValues["someObj"].Should().NotBeNull(); someObj2 =JsonSerializer.Deserialize(_mockPipeline.LoginRequest.RequestObjectValues["someObj"], someObj.GetType()); someObj.Should().BeEquivalentTo(someObj2); _mockPipeline.LoginRequest.RequestObjectValues["someArr"].Should().NotBeNull(); someArr2 =JsonSerializer.Deserialize(_mockPipeline.LoginRequest.Parameters["someArr"]); someArr2.Should().Contain(new[] { "a", "c", "b" }); someArr2.Length.Should().Be(3); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_jwt_request_without_client_id() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Invalid client_id"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_jwt_request_without_client_id_in_jwt() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request_object"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Invalid JWT request"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_jwt_request_if_audience_is_incorrect() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: "invalid", credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request_object"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Invalid JWT request"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_jwt_request_if_issuer_does_not_match_client_id() { var requestJwt = CreateRequestJwt( issuer: "invalid", audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request_object"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Invalid JWT request"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_jwt_request_that_includes_request_param() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), new Claim("request", "request") }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request_object"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Invalid JWT request"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_jwt_request_that_includes_request_uri_param() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), new Claim("request_uri", "request_uri") }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request_object"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Invalid JWT request"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_jwt_request_if_response_type_does_not_match() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("response_type", "id_token token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo") }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Invalid JWT request"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_jwt_request_if_client_id_does_not_match() { var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("response_type", "id_token"), new Claim("client_id", "client"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo") }); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token", extra: new { request = requestJwt }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request"); _mockPipeline.ErrorMessage.ErrorDescription.Should().Be("Invalid JWT request"); _mockPipeline.LoginRequest.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_ignore_request_uri_when_feature_is_disabled() { _mockPipeline.Options.Endpoints.EnableJwtRequestUri = false; var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); _mockPipeline.JwtRequestMessageHandler.OnInvoke = req => { req.RequestUri.Should().Be(new Uri("http://client_jwt")); return Task.CompletedTask; }; _mockPipeline.JwtRequestMessageHandler.Response.Content = new StringContent(requestJwt); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request_uri = "http://client_jwt" }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.JwtRequestMessageHandler.InvokeWasCalled.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_accept_request_uri_with_valid_jwt() { _mockPipeline.Options.Endpoints.EnableJwtRequestUri = true; var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); _mockPipeline.JwtRequestMessageHandler.OnInvoke = req => { req.RequestUri.Should().Be(new Uri("http://client_jwt")); return Task.CompletedTask; }; _mockPipeline.JwtRequestMessageHandler.Response.Content = new StringContent(requestJwt); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request_uri = "http://client_jwt" }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginRequest.Should().NotBeNull(); _mockPipeline.LoginRequest.Client.ClientId.Should().Be(_client.ClientId); _mockPipeline.LoginRequest.DisplayMode.Should().Be("popup"); _mockPipeline.LoginRequest.UiLocales.Should().Be("ui_locale_value"); _mockPipeline.LoginRequest.IdP.Should().Be("idp_value"); _mockPipeline.LoginRequest.Tenant.Should().Be("tenant_value"); _mockPipeline.LoginRequest.LoginHint.Should().Be("login_hint_value"); _mockPipeline.LoginRequest.AcrValues.Should().BeEquivalentTo(new string[] { "acr_2", "acr_1" }); _mockPipeline.LoginRequest.Parameters.AllKeys.Should().Contain("foo"); _mockPipeline.LoginRequest.Parameters["foo"].Should().Be("123foo"); _mockPipeline.JwtRequestMessageHandler.InvokeWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_accept_request_uri_with_valid_jwt_and_strict_validation() { _mockPipeline.Options.Endpoints.EnableJwtRequestUri = true; _mockPipeline.Options.StrictJarValidation = true; var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }, setJwtTyp: true); _mockPipeline.JwtRequestMessageHandler.OnInvoke = req => { req.RequestUri.Should().Be(new Uri("http://client_jwt")); return Task.CompletedTask; }; _mockPipeline.JwtRequestMessageHandler.Response.Content = new StringContent(requestJwt); _mockPipeline.JwtRequestMessageHandler.Response.Content.Headers.ContentType = new MediaTypeHeaderValue($"application/{JwtClaimTypes.JwtTypes.AuthorizationRequest}"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request_uri = "http://client_jwt" }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginRequest.Should().NotBeNull(); _mockPipeline.LoginRequest.Client.ClientId.Should().Be(_client.ClientId); _mockPipeline.LoginRequest.DisplayMode.Should().Be("popup"); _mockPipeline.LoginRequest.UiLocales.Should().Be("ui_locale_value"); _mockPipeline.LoginRequest.IdP.Should().Be("idp_value"); _mockPipeline.LoginRequest.Tenant.Should().Be("tenant_value"); _mockPipeline.LoginRequest.LoginHint.Should().Be("login_hint_value"); _mockPipeline.LoginRequest.AcrValues.Should().BeEquivalentTo(new string[] { "acr_2", "acr_1" }); _mockPipeline.LoginRequest.Parameters.AllKeys.Should().Contain("foo"); _mockPipeline.LoginRequest.Parameters["foo"].Should().Be("123foo"); _mockPipeline.JwtRequestMessageHandler.InvokeWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task authorize_should_reject_request_uri_with_valid_jwt_and_strict_validation_but_invalid_content_type() { _mockPipeline.Options.Endpoints.EnableJwtRequestUri = true; _mockPipeline.Options.StrictJarValidation = true; var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }, setJwtTyp: true); _mockPipeline.JwtRequestMessageHandler.OnInvoke = req => { req.RequestUri.Should().Be(new Uri("http://client_jwt")); return Task.CompletedTask; }; _mockPipeline.JwtRequestMessageHandler.Response.Content = new StringContent(requestJwt); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request_uri = "http://client_jwt" }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorMessage.Error.Should().Be("invalid_request_uri"); _mockPipeline.LoginRequest.Should().BeNull(); _mockPipeline.JwtRequestMessageHandler.InvokeWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task request_uri_response_returns_500_should_fail() { _mockPipeline.Options.Endpoints.EnableJwtRequestUri = true; _mockPipeline.JwtRequestMessageHandler.Response = new HttpResponseMessage(System.Net.HttpStatusCode.InternalServerError); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request_uri = "http://client_jwt" }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.LoginRequest.Should().BeNull(); _mockPipeline.JwtRequestMessageHandler.InvokeWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task request_uri_response_returns_404_should_fail() { _mockPipeline.Options.Endpoints.EnableJwtRequestUri = true; _mockPipeline.JwtRequestMessageHandler.Response = new HttpResponseMessage(System.Net.HttpStatusCode.NotFound); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request_uri = "http://client_jwt" }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.LoginRequest.Should().BeNull(); _mockPipeline.JwtRequestMessageHandler.InvokeWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task request_uri_length_too_long_should_fail() { _mockPipeline.Options.Endpoints.EnableJwtRequestUri = true; var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request_uri = "http://" + new string('x', 512) }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.LoginRequest.Should().BeNull(); _mockPipeline.JwtRequestMessageHandler.InvokeWasCalled.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task both_request_and_request_uri_params_should_fail() { _mockPipeline.Options.Endpoints.EnableJwtRequestUri = true; var requestJwt = CreateRequestJwt( issuer: _client.ClientId, audience: IdentityServerPipeline.BaseUrl, credential: new X509SigningCredentials(TestCert.Load()), claims: new[] { new Claim("client_id", _client.ClientId), new Claim("response_type", "id_token"), new Claim("scope", "openid profile"), new Claim("state", "123state"), new Claim("nonce", "123nonce"), new Claim("redirect_uri", "https://client/callback"), new Claim("acr_values", "acr_1 acr_2 tenant:tenant_value idp:idp_value"), new Claim("login_hint", "login_hint_value"), new Claim("display", "popup"), new Claim("ui_locales", "ui_locale_value"), new Claim("foo", "123foo"), }); _mockPipeline.JwtRequestMessageHandler.Response.Content = new StringContent(requestJwt); var url = _mockPipeline.CreateAuthorizeUrl( clientId: _client.ClientId, responseType: "id_token", extra: new { request = requestJwt, request_uri = "http://client_jwt" }); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); _mockPipeline.LoginRequest.Should().BeNull(); _mockPipeline.JwtRequestMessageHandler.InvokeWasCalled.Should().BeFalse(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/RestrictAccessTokenViaBrowserTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Net; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.IntegrationTests.Common; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.Authorize; public class RestrictAccessTokenViaBrowserTests { private const string Category = "RestrictAccessTokenViaBrowserTests"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); private ClaimsPrincipal _user = new IdentityServerUser("bob").CreatePrincipal(); public RestrictAccessTokenViaBrowserTests() { _mockPipeline.Clients.AddRange(new Client[] { new Client { ClientId = "client1", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client1/callback" }, AllowAccessTokensViaBrowser = true }, new Client { ClientId = "client2", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client2/callback" }, AllowAccessTokensViaBrowser = false }, new Client { ClientId = "client3", AllowedGrantTypes = GrantTypes.Hybrid, ClientSecrets = { new Secret("secret".Sha256()) }, RequireConsent = false, RequirePkce = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client3/callback" }, AllowAccessTokensViaBrowser = true }, new Client { ClientId = "client4", AllowedGrantTypes = GrantTypes.Hybrid, ClientSecrets = { new Secret("secret".Sha256()) }, RequireConsent = false, RequirePkce = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client4/callback" }, AllowAccessTokensViaBrowser = false } }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId() }); _mockPipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task Unrestricted_implicit_client_can_request_IdToken() { await _mockPipeline.LoginAsync(_user); var url = _mockPipeline.CreateAuthorizeUrl("client1", "id_token", "openid", "https://client1/callback", "state", "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Found); response.Headers.Location.AbsoluteUri.Should().StartWith("https://client1/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IdentityToken.Should().NotBeNull(); authorization.AccessToken.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task Unrestricted_implicit_client_can_request_IdTokenToken() { await _mockPipeline.LoginAsync(_user); var url = _mockPipeline.CreateAuthorizeUrl("client1", "id_token token", "openid", "https://client1/callback", "state", "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Found); response.Headers.Location.AbsoluteUri.Should().StartWith("https://client1/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IdentityToken.Should().NotBeNull(); authorization.AccessToken.Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task Restricted_implicit_client_can_request_IdToken() { await _mockPipeline.LoginAsync(_user); var url = _mockPipeline.CreateAuthorizeUrl("client2", "id_token", "openid", "https://client2/callback", "state", "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Found); response.Headers.Location.AbsoluteUri.Should().StartWith("https://client2/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IdentityToken.Should().NotBeNull(); authorization.AccessToken.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task Restricted_implicit_client_cannot_request_IdTokenToken() { await _mockPipeline.LoginAsync(_user); var url = _mockPipeline.CreateAuthorizeUrl("client2", "id_token token", "openid", "https://client2/callback", "state", "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = true; var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Unrestricted_hybrid_client_can_request_CodeIdToken() { await _mockPipeline.LoginAsync(_user); var url = _mockPipeline.CreateAuthorizeUrl("client3", "code id_token", "openid", "https://client3/callback", "state", "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Found); response.Headers.Location.AbsoluteUri.Should().StartWith("https://client3/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IdentityToken.Should().NotBeNull(); authorization.AccessToken.Should().BeNull(); authorization.Code.Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task Unrestricted_hybrid_client_can_request_CodeIdTokenToken() { await _mockPipeline.LoginAsync(_user); var url = _mockPipeline.CreateAuthorizeUrl("client3", "code id_token token", "openid", "https://client3/callback", "state", "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Found); response.Headers.Location.AbsoluteUri.Should().StartWith("https://client3/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IdentityToken.Should().NotBeNull(); authorization.AccessToken.Should().NotBeNull(); authorization.Code.Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task Restricted_hybrid_client_can_request_CodeIdToken() { await _mockPipeline.LoginAsync(_user); var url = _mockPipeline.CreateAuthorizeUrl("client4", "code id_token", "openid", "https://client4/callback", "state", "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Found); response.Headers.Location.AbsoluteUri.Should().StartWith("https://client4/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IdentityToken.Should().NotBeNull(); authorization.AccessToken.Should().BeNull(); authorization.Code.Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task Restricted_hybrid_client_cannot_request_CodeIdTokenToken() { await _mockPipeline.LoginAsync(_user); var url = _mockPipeline.CreateAuthorizeUrl("client4", "code id_token token", "openid", "https://client4/callback", "state", "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = true; var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.ErrorWasCalled.Should().BeTrue(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Authorize/SessionIdTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.Authorize; public class SessionIdTests { private const string Category = "SessionIdTests"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public SessionIdTests() { _mockPipeline.Clients.AddRange(new Client[] { new Client { ClientId = "client1", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid", "profile" }, RedirectUris = new List { "https://client1/callback" }, AllowAccessTokensViaBrowser = true }, new Client { ClientId = "client2", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = true, AllowedScopes = new List { "openid", "profile", "api1", "api2" }, RedirectUris = new List { "https://client2/callback" }, AllowAccessTokensViaBrowser = true } }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email() }); _mockPipeline.ApiResources.AddRange(new ApiResource[] { new ApiResource { Name = "api", } }); _mockPipeline.ApiScopes.AddRange(new ApiScope[] { new ApiScope { Name = "api1" }, new ApiScope { Name = "api2" } }); _mockPipeline.Initialize(); } [Fact] public async Task session_id_should_be_reissued_if_session_cookie_absent() { await _mockPipeline.LoginAsync("bob"); var sid1 = _mockPipeline.GetSessionCookie().Value; sid1.Should().NotBeNull(); _mockPipeline.RemoveSessionCookie(); await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.DiscoveryEndpoint); var sid2 = _mockPipeline.GetSessionCookie().Value; sid2.Should().Be(sid1); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/CheckSession/CheckSessionTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Net; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.IntegrationTests.Common; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.CheckSession; public class CheckSessionTests { private const string Category = "Check session endpoint"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public CheckSessionTests() { _mockPipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task get_request_should_not_return_404() { var response = await _mockPipeline.BackChannelClient.GetAsync(IdentityServerPipeline.CheckSessionEndpoint); response.StatusCode.Should().NotBe(HttpStatusCode.NotFound); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/DeviceAuthorization/DeviceAuthorizationTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.IO; using System.Net; using System.Net.Http; using System.Text; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using Newtonsoft.Json; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.DeviceAuthorization; public class DeviceAuthorizationTests { private const string Category = "Device authorization endpoint"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public DeviceAuthorizationTests() { _mockPipeline.Clients.Add(new Client { ClientId = "client1", ClientSecrets = {new Secret("secret".Sha256())}, AllowedGrantTypes = GrantTypes.DeviceFlow, AllowedScopes = {"openid"} }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId() }); _mockPipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task get_should_return_InvalidRequest() { var response = await _mockPipeline.BackChannelClient.GetAsync(IdentityServerPipeline.DeviceAuthorization); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var resultDto = ParseJsonBody(await response.Content.ReadAsStreamAsync()); resultDto.Should().NotBeNull(); resultDto.error.Should().Be(OidcConstants.TokenErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task wrong_content_type_return_InvalidRequest() { var form = new Dictionary { {"client_id", Guid.NewGuid().ToString()} }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.DeviceAuthorization, new StringContent(@"{""client_id"": ""client1""}", Encoding.UTF8, "application/json")); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var resultDto = ParseJsonBody(await response.Content.ReadAsStreamAsync()); resultDto.Should().NotBeNull(); resultDto.error.Should().Be(OidcConstants.TokenErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task empty_request_should_return_InvalidClient() { var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.DeviceAuthorization, new FormUrlEncodedContent(new Dictionary())); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var resultDto = ParseJsonBody(await response.Content.ReadAsStreamAsync()); resultDto.Should().NotBeNull(); resultDto.error.Should().Be(OidcConstants.TokenErrors.InvalidClient); } [Fact] [Trait("Category", Category)] public async Task unknown_client_should_return_InvalidClient() { var form = new Dictionary { {"client_id", "client1"} }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.DeviceAuthorization, new FormUrlEncodedContent(form)); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var resultDto = ParseJsonBody(await response.Content.ReadAsStreamAsync()); resultDto.Should().NotBeNull(); resultDto.error.Should().Be(OidcConstants.TokenErrors.InvalidClient); } [Fact] [Trait("Category", Category)] public async Task valid_should_return_json() { var form = new Dictionary { {"client_id", "client1"}, {"client_secret", "secret" } }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.DeviceAuthorization, new FormUrlEncodedContent(form)); response.StatusCode.Should().Be(HttpStatusCode.OK); response.Content.Headers.ContentType.MediaType.Should().Be("application/json"); var resultDto = ParseJsonBody(await response.Content.ReadAsStreamAsync()); resultDto.Should().NotBeNull(); resultDto.Should().NotBeNull(); resultDto.device_code.Should().NotBeNull(); resultDto.user_code.Should().NotBeNull(); resultDto.verification_uri.Should().NotBeNull(); resultDto.verification_uri_complete.Should().NotBeNull(); resultDto.expires_in.Should().BeGreaterThan(0); resultDto.interval.Should().BeGreaterThan(0); } private T ParseJsonBody(Stream streamBody) { streamBody.Position = 0; using (var reader = new StreamReader(streamBody)) { var jsonString = reader.ReadToEnd(); return JsonConvert.DeserializeObject(jsonString); } } internal class ResultDto { public string device_code { get; set; } public string user_code { get; set; } public string verification_uri { get; set; } public string verification_uri_complete { get; set; } public int expires_in { get; set; } public int interval { get; set; } } internal class ErrorResultDto { public string error { get; set; } public string error_description { get; set; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Discovery/DiscoveryEndpointTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using Newtonsoft.Json.Linq; using System.Linq; using System.Threading.Tasks; using Xunit; using JsonWebKey = Microsoft.IdentityModel.Tokens.JsonWebKey; namespace IdentityServer.IntegrationTests.Endpoints.Discovery; public class DiscoveryEndpointTests { private const string Category = "Discovery endpoint"; [Fact] [Trait("Category", Category)] public async Task Issuer_uri_should_be_lowercase() { IdentityServerPipeline pipeline = new IdentityServerPipeline(); pipeline.Initialize("/ROOT"); var result = await pipeline.BackChannelClient.GetAsync("HTTPS://SERVER/ROOT/.WELL-KNOWN/OPENID-CONFIGURATION"); var json = await result.Content.ReadAsStringAsync(); var data = JObject.Parse(json); var issuer = data["issuer"].ToString(); issuer.Should().Be("https://server/root"); } [Fact] [Trait("Category", Category)] public async Task when_lower_case_issuer_option_disabled_issuer_uri_should_be_preserved() { IdentityServerPipeline pipeline = new IdentityServerPipeline(); pipeline.Initialize("/ROOT"); pipeline.Options.LowerCaseIssuerUri = false; var result = await pipeline.BackChannelClient.GetAsync("HTTPS://SERVER/ROOT/.WELL-KNOWN/OPENID-CONFIGURATION"); var json = await result.Content.ReadAsStringAsync(); var data = JObject.Parse(json); var issuer = data["issuer"].ToString(); issuer.Should().Be("https://server/ROOT"); } private void Pipeline_OnPostConfigureServices(IServiceCollection obj) { throw new System.NotImplementedException(); } [Fact] [Trait("Category", Category)] public async Task Algorithms_supported_should_match_signing_key() { var key = CryptoHelper.CreateECDsaSecurityKey(JsonWebKeyECTypes.P256); var expectedAlgorithm = SecurityAlgorithms.EcdsaSha256; IdentityServerPipeline pipeline = new IdentityServerPipeline(); pipeline.OnPostConfigureServices += services => { // add key to standard RSA key services.AddIdentityServerBuilder() .AddSigningCredential(key, expectedAlgorithm); }; pipeline.Initialize("/ROOT"); var result = await pipeline.BackChannelClient.GetAsync("https://server/root/.well-known/openid-configuration"); var json = await result.Content.ReadAsStringAsync(); var data = JObject.Parse(json); var algorithmsSupported = data["id_token_signing_alg_values_supported"]; algorithmsSupported.Count().Should().Be(2); algorithmsSupported.Values().Should().Contain(SecurityAlgorithms.RsaSha256); algorithmsSupported.Values().Should().Contain(SecurityAlgorithms.EcdsaSha256); } [Fact] [Trait("Category", Category)] public async Task Jwks_entries_should_countain_crv() { var ecdsaKey = CryptoHelper.CreateECDsaSecurityKey(JsonWebKeyECTypes.P256); var parameters = ecdsaKey.ECDsa.ExportParameters(true); IdentityServerPipeline pipeline = new IdentityServerPipeline(); var jsonWebKeyFromECDsa = new JsonWebKey() { Kty = JsonWebAlgorithmsKeyTypes.EllipticCurve, Use = "sig", Kid = ecdsaKey.KeyId, KeyId = ecdsaKey.KeyId, X = Base64UrlEncoder.Encode(parameters.Q.X), Y = Base64UrlEncoder.Encode(parameters.Q.Y), D = Base64UrlEncoder.Encode(parameters.D), Crv = JsonWebKeyECTypes.P256, Alg = SecurityAlgorithms.EcdsaSha256 }; pipeline.OnPostConfigureServices += services => { // add ECDsa as JsonWebKey services.AddIdentityServerBuilder() .AddSigningCredential(jsonWebKeyFromECDsa, SecurityAlgorithms.EcdsaSha256); }; pipeline.Initialize("/ROOT"); var result = await pipeline.BackChannelClient.GetAsync("https://server/root/.well-known/openid-configuration/jwks"); var json = await result.Content.ReadAsStringAsync(); var data = JObject.Parse(json); var keys = data["keys"]; keys.Should().NotBeNull(); var key = keys[1]; key.Should().NotBeNull(); var crv = key["crv"]; crv.Should().NotBeNull(); crv.Value().Should().Be(JsonWebKeyECTypes.P256); } [Fact] [Trait("Category", Category)] public async Task Jwks_entries_should_contain_alg() { IdentityServerPipeline pipeline = new IdentityServerPipeline(); pipeline.Initialize("/ROOT"); var result = await pipeline.BackChannelClient.GetAsync("https://server/root/.well-known/openid-configuration/jwks"); var json = await result.Content.ReadAsStringAsync(); var data = JObject.Parse(json); var keys = data["keys"]; keys.Should().NotBeNull(); var key = keys[0]; key.Should().NotBeNull(); var alg = key["alg"]; alg.Should().NotBeNull(); alg.Value().Should().Be(Constants.SigningAlgorithms.RSA_SHA_256); } [Theory] [InlineData(JsonWebKeyECTypes.P256, SecurityAlgorithms.EcdsaSha256)] [InlineData(JsonWebKeyECTypes.P384, SecurityAlgorithms.EcdsaSha384)] [InlineData(JsonWebKeyECTypes.P521, SecurityAlgorithms.EcdsaSha512)] [Trait("Category", Category)] public async Task Jwks_with_ecdsa_should_have_parsable_key(string crv, string alg) { var key = CryptoHelper.CreateECDsaSecurityKey(crv); IdentityServerPipeline pipeline = new IdentityServerPipeline(); pipeline.OnPostConfigureServices += services => { services.AddIdentityServerBuilder() .AddSigningCredential(key, alg); }; pipeline.Initialize("/ROOT"); var result = await pipeline.BackChannelClient.GetAsync("https://server/root/.well-known/openid-configuration/jwks"); var json = await result.Content.ReadAsStringAsync(); var jwks = new JsonWebKeySet(json); var parsedKeys = jwks.GetSigningKeys(); var matchingKey = parsedKeys.FirstOrDefault(x => x.KeyId == key.KeyId); matchingKey.Should().NotBeNull(); matchingKey.Should().BeOfType(); } [Fact] public async Task Jwks_with_two_key_using_different_algs_expect_different_alg_values() { var ecdsaKey = CryptoHelper.CreateECDsaSecurityKey(); var rsaKey = CryptoHelper.CreateRsaSecurityKey(); IdentityServerPipeline pipeline = new IdentityServerPipeline(); pipeline.OnPostConfigureServices += services => { services.AddIdentityServerBuilder() .AddSigningCredential(ecdsaKey, "ES256") .AddValidationKey(new SecurityKeyInfo { Key = rsaKey, SigningAlgorithm = "RS256" }); }; pipeline.Initialize("/ROOT"); var result = await pipeline.BackChannelClient.GetAsync("https://server/root/.well-known/openid-configuration/jwks"); var json = await result.Content.ReadAsStringAsync(); var jwks = new JsonWebKeySet(json); jwks.Keys.Should().Contain(x => x.KeyId == ecdsaKey.KeyId && x.Alg == "ES256"); jwks.Keys.Should().Contain(x => x.KeyId == rsaKey.KeyId && x.Alg == "RS256"); } [Fact] [Trait("Category", Category)] public async Task Unicode_values_in_url_should_be_processed_correctly() { var pipeline = new IdentityServerPipeline(); pipeline.Initialize(); var result = await pipeline.BackChannelClient.GetDiscoveryDocumentAsync(new DiscoveryDocumentRequest { Address = "https://грант.рф", Policy = { ValidateIssuerName = false, ValidateEndpoints = false, RequireHttps = false, RequireKeySet = false } }); result.Issuer.Should().Be("https://грант.рф"); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/EndSession/EndSessionTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Text; using System.Text.Encodings.Web; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Test; using Microsoft.AspNetCore.WebUtilities; using Newtonsoft.Json.Linq; using Xunit; using static IdentityServer8.IdentityServerConstants; namespace IdentityServer.IntegrationTests.Endpoints.EndSession; public class EndSessionTests { private const string Category = "End session endpoint"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); private Client _wsfedClient; public EndSessionTests() { _mockPipeline.Clients.Add(new Client { ClientId = "client1", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client1/callback" }, FrontChannelLogoutUri = "https://client1/signout", PostLogoutRedirectUris = new List { "https://client1/signout-callback" }, AllowAccessTokensViaBrowser = true }); _mockPipeline.Clients.Add(new Client { ClientId = "client2", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client2/callback" }, FrontChannelLogoutUri = "https://client2/signout", PostLogoutRedirectUris = new List { "https://client2/signout-callback", "https://client2/signout-callback2" }, AllowAccessTokensViaBrowser = true }); _mockPipeline.Clients.Add(new Client { ClientId = "client3", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client3/callback" }, BackChannelLogoutUri = "https://client3/signout", AllowAccessTokensViaBrowser = true }); _mockPipeline.Clients.Add(_wsfedClient = new Client { ClientId = "client4", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client4/callback" }, FrontChannelLogoutUri = "https://client4/signout", AllowAccessTokensViaBrowser = true }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId() }); _mockPipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task get_request_should_not_return_404() { var response = await _mockPipeline.BackChannelClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); response.StatusCode.Should().NotBe(HttpStatusCode.NotFound); } [Fact] [Trait("Category", Category)] public async Task signout_request_should_redirect_to_logout_page() { var response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); _mockPipeline.LogoutWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task get_request_should_redirect_to_configured_logout_path() { _mockPipeline.Options.UserInteraction.LogoutUrl = "/logout"; _mockPipeline.Options.UserInteraction.LogoutIdParameter = "id"; await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); var id_token = authorization.IdentityToken; response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint + "?id_token_hint=" + id_token + "&post_logout_redirect_uri=https://client1/signout-callback"); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://server/logout?id="); } [Fact] [Trait("Category", Category)] public async Task logout_request_with_params_should_pass_values_in_logout_context() { await _mockPipeline.LoginAsync("bob"); var authorization = await _mockPipeline.RequestAuthorizationEndpointAsync( clientId: "client2", responseType: "id_token", scope: "openid", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); var id_token = authorization.IdentityToken; var response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint + "?id_token_hint=" + id_token + "&post_logout_redirect_uri=https://client2/signout-callback2"); _mockPipeline.LogoutWasCalled.Should().BeTrue(); _mockPipeline.LogoutRequest.Should().NotBeNull(); _mockPipeline.LogoutRequest.ClientId.Should().Be("client2"); _mockPipeline.LogoutRequest.PostLogoutRedirectUri.Should().Be("https://client2/signout-callback2"); var parts = _mockPipeline.LogoutRequest.SignOutIFrameUrl.Split('?'); parts[0].Should().Be(IdentityServerPipeline.EndSessionCallbackEndpoint); var iframeUrl = QueryHelpers.ParseNullableQuery(parts[1]); iframeUrl["endSessionId"].FirstOrDefault().Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task logout_request_with_params_but_user_no_longer_authenticated_should_pass_redirect_info_to_logout() { await _mockPipeline.LoginAsync("bob"); var authorization = await _mockPipeline.RequestAuthorizationEndpointAsync( clientId: "client2", responseType: "id_token", scope: "openid", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); var id_token = authorization.IdentityToken; _mockPipeline.RemoveLoginCookie(); var response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint + "?id_token_hint=" + id_token + "&post_logout_redirect_uri=https://client2/signout-callback2"); _mockPipeline.LogoutWasCalled.Should().BeTrue(); _mockPipeline.LogoutRequest.Should().NotBeNull(); _mockPipeline.LogoutRequest.ClientId.Should().Be("client2"); _mockPipeline.LogoutRequest.PostLogoutRedirectUri.Should().Be("https://client2/signout-callback2"); _mockPipeline.LogoutRequest.SignOutIFrameUrl.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task signout_should_support_POST() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); var id_token = authorization.IdentityToken; _mockPipeline.BrowserClient.AllowAutoRedirect = true; var values = new List>(); values.Add(new KeyValuePair("id_token_hint", id_token)); values.Add(new KeyValuePair("post_logout_redirect_uri", "https://client1/signout-callback")); var content = new FormUrlEncodedContent(values); response = await _mockPipeline.BrowserClient.PostAsync(IdentityServerPipeline.EndSessionEndpoint, content); _mockPipeline.LogoutWasCalled.Should().BeTrue(); _mockPipeline.LogoutRequest.Should().NotBeNull(); _mockPipeline.LogoutRequest.ClientId.Should().Be("client1"); _mockPipeline.LogoutRequest.PostLogoutRedirectUri.Should().Be("https://client1/signout-callback"); var parts = _mockPipeline.LogoutRequest.SignOutIFrameUrl.Split('?'); parts[0].Should().Be(IdentityServerPipeline.EndSessionCallbackEndpoint); var iframeUrl = QueryHelpers.ParseNullableQuery(parts[1]); iframeUrl["endSessionId"].FirstOrDefault().Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task signout_callback_without_params_should_return_400() { var response = await _mockPipeline.BackChannelClient.GetAsync(IdentityServerPipeline.EndSessionCallbackEndpoint); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); } [Fact] [Trait("Category", Category)] public async Task signout_callback_with_mismatched_post_logout_redirect_uri_should_not_pass_along_logout_uri() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); var id_token = authorization.IdentityToken; _mockPipeline.BrowserClient.AllowAutoRedirect = true; response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint + "?id_token_hint=" + id_token + "&post_logout_redirect_uri=https://client1/signout-callback-not-valid"); var signoutFrameUrl = _mockPipeline.LogoutRequest.SignOutIFrameUrl; response = await _mockPipeline.BrowserClient.GetAsync(signoutFrameUrl); _mockPipeline.LogoutRequest.ClientId.Should().NotBeNull(); _mockPipeline.LogoutRequest.PostLogoutRedirectUri.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task signout_callback_with_mismatched_id_token_hint_should_not_pass_along_logout_message() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); var id_token = authorization.IdentityToken; await _mockPipeline.LoginAsync("alice"); _mockPipeline.BrowserClient.AllowAutoRedirect = true; response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint + "?id_token_hint=" + id_token + "&post_logout_redirect_uri=https://client1/signout-callback"); _mockPipeline.LogoutRequest.ClientId.Should().BeNull(); _mockPipeline.LogoutRequest.PostLogoutRedirectUri.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task valid_signout_callback_should_return_200_html() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.BrowserClient.AllowAutoRedirect = true; response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); var signoutFrameUrl = _mockPipeline.LogoutRequest.SignOutIFrameUrl; response = await _mockPipeline.BrowserClient.GetAsync(signoutFrameUrl); response.StatusCode.Should().Be(HttpStatusCode.OK); response.Content.Headers.ContentType.MediaType.Should().Be("text/html"); } [Fact] [Trait("Category", Category)] public async Task valid_signout_callback_should_render_iframes_for_all_clients() { await _mockPipeline.LoginAsync("bob"); var sid = _mockPipeline.GetSessionCookie().Value; _mockPipeline.BrowserClient.AllowAutoRedirect = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); var url2 = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token", scope: "openid", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); var response2 = await _mockPipeline.BrowserClient.GetAsync(url2); _mockPipeline.BrowserClient.AllowAutoRedirect = true; response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); var signoutFrameUrl = _mockPipeline.LogoutRequest.SignOutIFrameUrl; response = await _mockPipeline.BrowserClient.GetAsync(signoutFrameUrl); var html = await response.Content.ReadAsStringAsync(); html.Should().Contain(HtmlEncoder.Default.Encode("https://client1/signout?sid=" + sid + "&iss=" + UrlEncoder.Default.Encode("https://server"))); html.Should().Contain(HtmlEncoder.Default.Encode("https://client2/signout?sid=" + sid + "&iss=" + UrlEncoder.Default.Encode("https://server"))); } [Fact] [Trait("Category", Category)] public async Task signout_callback_should_use_signoutcleanup_for_wsfed_client() { await _mockPipeline.LoginAsync("bob"); var sid = _mockPipeline.GetSessionCookie().Value; _mockPipeline.BrowserClient.AllowAutoRedirect = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client4", responseType: "id_token", scope: "openid", redirectUri: "https://client4/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.BrowserClient.AllowAutoRedirect = true; response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); var signoutFrameUrl = _mockPipeline.LogoutRequest.SignOutIFrameUrl; // since we don't have real ws-fed, we used OIDC to signin, but fooling this // at signout to use ws-fed so we can test the iframe params _wsfedClient.ProtocolType = ProtocolTypes.WsFederation; response = await _mockPipeline.BrowserClient.GetAsync(signoutFrameUrl); var html = await response.Content.ReadAsStringAsync(); html.Should().Contain("https://client4/signout?wa=wsignoutcleanup1.0"); } [Fact] [Trait("Category", Category)] public async Task valid_id_token_hint_but_no_post_logout_redirect_uri_should_not_use_single_registered_post_logout_redirect_uri() { await _mockPipeline.LoginAsync("bob"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); var id_token = authorization.IdentityToken; _mockPipeline.BrowserClient.AllowAutoRedirect = true; response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint + "?id_token_hint=" + id_token); _mockPipeline.LogoutRequest.PostLogoutRedirectUri.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task valid_id_token_hint_but_no_post_logout_redirect_uri_should_not_use_any_of_multiple_registered_post_logout_redirect_uri() { await _mockPipeline.LoginAsync("bob"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var url = _mockPipeline.CreateAuthorizeUrl( clientId: "client2", responseType: "id_token", scope: "openid", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); var id_token = authorization.IdentityToken; _mockPipeline.BrowserClient.AllowAutoRedirect = true; response = await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint + "?id_token_hint=" + id_token); _mockPipeline.LogoutRequest.PostLogoutRedirectUri.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task logout_with_clients_should_render_signout_callback_iframe() { await _mockPipeline.LoginAsync("bob"); var response = await _mockPipeline.RequestAuthorizationEndpointAsync( clientId: "client2", responseType: "id_token", scope: "openid", redirectUri: "https://client2/callback", state: "123_state", nonce: "123_nonce"); response.Should().NotBeNull(); await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); _mockPipeline.LogoutWasCalled.Should().BeTrue(); _mockPipeline.LogoutRequest.SignOutIFrameUrl.Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task logout_without_clients_should_not_render_signout_callback_iframe() { await _mockPipeline.LoginAsync("bob"); await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); _mockPipeline.LogoutWasCalled.Should().BeTrue(); _mockPipeline.LogoutRequest.SignOutIFrameUrl.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task logout_should_invoke_back_channel_logout() { _mockPipeline.BackChannelMessageHandler.OnInvoke = async req => { req.RequestUri.ToString().Should().StartWith("https://client3/signout"); var form = await req.Content.ReadAsStringAsync(); form.Should().Contain(OidcConstants.BackChannelLogoutRequest.LogoutToken); var token = form.Split('=')[1]; var parts = token.Split('.'); parts.Length.Should().Be(3); var bytes = Base64Url.Decode(parts[1]); var json = Encoding.UTF8.GetString(bytes); var payload = JObject.Parse(json); payload["iss"].ToString().Should().Be("https://server"); payload["sub"].ToString().Should().Be("bob"); payload["aud"].ToString().Should().Be("client3"); payload["iat"].Should().NotBeNull(); payload["jti"].Should().NotBeNull(); payload["sid"].Should().NotBeNull(); payload["events"].Type.Should().Be(JTokenType.Object); var events = (JObject)payload["events"]; events.Count.Should().Be(1); events["http://schemas.openid.net/event/backchannel-logout"].Should().NotBeNull(); events["http://schemas.openid.net/event/backchannel-logout"].Type.Should().Be(JTokenType.Object); var evt = (JObject)events["http://schemas.openid.net/event/backchannel-logout"]; evt.Count.Should().Be(0); }; await _mockPipeline.LoginAsync("bob"); var response = await _mockPipeline.RequestAuthorizationEndpointAsync( clientId: "client3", responseType: "id_token", scope: "openid", redirectUri: "https://client3/callback", state: "123_state", nonce: "123_nonce"); response.Should().NotBeNull(); await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); _mockPipeline.BackChannelMessageHandler.InvokeWasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task back_channel_logout_should_not_affect_end_session_callback() { _mockPipeline.BackChannelMessageHandler.OnInvoke = req => throw new Exception("boom!"); await _mockPipeline.LoginAsync("bob"); var response = await _mockPipeline.RequestAuthorizationEndpointAsync( clientId: "client3", responseType: "id_token", scope: "openid", redirectUri: "https://client3/callback", state: "123_state", nonce: "123_nonce"); response.Should().NotBeNull(); await _mockPipeline.BrowserClient.GetAsync(IdentityServerPipeline.EndSessionEndpoint); _mockPipeline.BackChannelMessageHandler.InvokeWasCalled.Should().BeTrue(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/IntrospectionTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Net; using System.Net.Http; using System.Text; using System.Text.Json; using System.Threading.Tasks; using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Endpoints.Introspection.Setup; using IdentityServer8.Extensions; using Microsoft.AspNetCore.Hosting; using Microsoft.AspNetCore.TestHost; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.Introspection; public class IntrospectionTests { private const string Category = "Introspection endpoint"; private const string IntrospectionEndpoint = "https://server/connect/introspect"; private const string TokenEndpoint = "https://server/connect/token"; private readonly HttpClient _client; private readonly HttpMessageHandler _handler; public IntrospectionTests() { var builder = new WebHostBuilder() .UseStartup(); var server = new TestServer(builder); _handler = server.CreateHandler(); _client = server.CreateClient(); } [Fact] [Trait("Category", Category)] public async Task Empty_request_should_fail() { var form = new Dictionary(); var response = await _client.PostAsync(IntrospectionEndpoint, new FormUrlEncodedContent(form)); response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] [Trait("Category", Category)] public async Task Unknown_scope_should_fail() { var form = new Dictionary(); _client.SetBasicAuthentication("unknown", "invalid"); var response = await _client.PostAsync(IntrospectionEndpoint, new FormUrlEncodedContent(form)); response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] [Trait("Category", Category)] public async Task Invalid_scope_secret_should_fail() { var form = new Dictionary(); _client.SetBasicAuthentication("api1", "invalid"); var response = await _client.PostAsync(IntrospectionEndpoint, new FormUrlEncodedContent(form)); response.StatusCode.Should().Be(HttpStatusCode.Unauthorized); } [Fact] [Trait("Category", Category)] public async Task Missing_token_should_fail() { var form = new Dictionary(); _client.SetBasicAuthentication("api1", "secret"); var response = await _client.PostAsync(IntrospectionEndpoint, new FormUrlEncodedContent(form)); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); } [Fact] [Trait("Category", Category)] public async Task Invalid_token_should_fail() { var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api1", ClientSecret = "secret", Token = "invalid" }); introspectionResponse.IsActive.Should().Be(false); introspectionResponse.IsError.Should().Be(false); } [Fact] [Trait("Category", Category)] public async Task Invalid_Content_type_should_fail() { var tokenResponse = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client1", ClientSecret = "secret", Scope = "api1" }); var data = new { client_id = "api1", client_secret = "secret", token = tokenResponse.AccessToken }; var json = JsonSerializer.Serialize(data); var client = new HttpClient(_handler); var response = await client.PostAsync(IntrospectionEndpoint, new StringContent(json, Encoding.UTF8, "application/json")); response.StatusCode.Should().Be(HttpStatusCode.UnsupportedMediaType); } [Fact] [Trait("Category", Category)] public async Task Valid_token_and_valid_scope_should_succeed() { var tokenResponse = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client1", ClientSecret = "secret", Scope = "api1" }); var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api1", ClientSecret = "secret", Token = tokenResponse.AccessToken }); introspectionResponse.IsActive.Should().Be(true); introspectionResponse.IsError.Should().Be(false); var scopes = from c in introspectionResponse.Claims where c.Type == "scope" select c; scopes.Count().Should().Be(1); scopes.First().Value.Should().Be("api1"); } [Fact] [Trait("Category", Category)] public async Task Response_data_should_be_valid_using_single_scope() { var tokenResponse = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client1", ClientSecret = "secret", Scope = "api1" }); var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api1", ClientSecret = "secret", Token = tokenResponse.AccessToken }); var values = introspectionResponse.Json.ToObject>(); values["aud"].ValueKind.Should().Be(JsonValueKind.String); values["iss"].ValueKind.Should().Be(JsonValueKind.String); values["nbf"].ValueKind.Should().Be(JsonValueKind.Number); values["exp"].ValueKind.Should().Be(JsonValueKind.Number); values["client_id"].ValueKind.Should().Be(JsonValueKind.String); values["active"].ValueKind.Should().Be(JsonValueKind.True); values["scope"].ValueKind.Should().Be(JsonValueKind.String); values["scope"].ToString().Should().Be("api1"); } [Fact] [Trait("Category", Category)] public async Task Response_data_with_user_authentication_should_be_valid_using_single_scope() { var tokenResponse = await _client.RequestPasswordTokenAsync(new PasswordTokenRequest { Address = TokenEndpoint, ClientId = "ro.client", ClientSecret = "secret", UserName = "bob", Password = "bob", Scope = "api1", }); tokenResponse.IsError.Should().BeFalse(); var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api1", ClientSecret = "secret", Token = tokenResponse.AccessToken }); var values = introspectionResponse.Json.ToObject>(); values["aud"].ValueKind.Should().Be(JsonValueKind.String); values["iss"].ValueKind.Should().Be(JsonValueKind.String); values["nbf"].ValueKind.Should().Be(JsonValueKind.Number); values["exp"].ValueKind.Should().Be(JsonValueKind.Number); values["auth_time"].ValueKind.Should().Be(JsonValueKind.Number); values["client_id"].ValueKind.Should().Be(JsonValueKind.String); values["sub"].ValueKind.Should().Be(JsonValueKind.String); values["active"].ValueKind.Should().Be(JsonValueKind.True); values["scope"].ValueKind.Should().Be(JsonValueKind.String); values["scope"].ToString().Should().Be("api1"); } [Fact] [Trait("Category", Category)] public async Task Response_data_should_be_valid_using_multiple_scopes_multiple_audiences() { var tokenResponse = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client1", ClientSecret = "secret", Scope = "api2 api3-a api3-b", }); tokenResponse.IsError.Should().BeFalse(); var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api3", ClientSecret = "secret", Token = tokenResponse.AccessToken }); var values = introspectionResponse.Json.ToObject>(); values["aud"].GetType().Name.Should().Be("JsonElement"); values["aud"].ValueKind.Should().Be(JsonValueKind.Array); // Access the 'aud' array var audiences = values["aud"].EnumerateArray(); foreach (var aud in audiences) { // Check each audience value to ensure it's a string aud.ValueKind.Should().Be(JsonValueKind.String); } values["iss"].ValueKind.Should().Be(JsonValueKind.String); values["iss"].ValueKind.Should().Be(JsonValueKind.String); values["nbf"].ValueKind.Should().Be(JsonValueKind.Number); values["exp"].ValueKind.Should().Be(JsonValueKind.Number); values["client_id"].ValueKind.Should().Be(JsonValueKind.String); var activeKind= values["active"].ValueKind; values["active"].ValueKind.Should().Be(JsonValueKind.True); values["scope"].ValueKind.Should().Be(JsonValueKind.String); var scopes = values["scope"].ToString(); scopes.Should().Be("api3-a api3-b"); } [Fact] [Trait("Category", Category)] public async Task Response_data_should_be_valid_using_multiple_scopes_single_audience() { var tokenResponse = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client1", ClientSecret = "secret", Scope = "api3-a api3-b", }); tokenResponse.IsError.Should().BeFalse(); var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api3", ClientSecret = "secret", Token = tokenResponse.AccessToken }); var values = introspectionResponse.Json.ToObject>(); values["aud"].ValueKind.Should().Be(JsonValueKind.String); values["aud"].ValueKind.Should().Be(JsonValueKind.String); values["iss"].ValueKind.Should().Be(JsonValueKind.String); values["nbf"].ValueKind.Should().Be(JsonValueKind.Number); values["exp"].ValueKind.Should().Be(JsonValueKind.Number); values["client_id"].ValueKind.Should().Be(JsonValueKind.String); values["active"].ValueKind.Should().Be(JsonValueKind.True); values["scope"].ValueKind.Should().Be(JsonValueKind.String); var scopes = values["scope"].ToString(); scopes.Should().Be("api3-a api3-b"); } [Fact] [Trait("Category", Category)] public async Task Token_with_many_scopes_but_api_should_only_see_its_own_scopes() { var tokenResponse = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client3", ClientSecret = "secret", Scope = "api1 api2 api3-a", }); tokenResponse.IsError.Should().BeFalse(); var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api3", ClientSecret = "secret", Token = tokenResponse.AccessToken }); introspectionResponse.IsActive.Should().BeTrue(); introspectionResponse.IsError.Should().BeFalse(); var scopes = from c in introspectionResponse.Claims where c.Type == "scope" select c.Value; scopes.Count().Should().Be(1); scopes.First().Should().Be("api3-a"); } [Fact] [Trait("Category", Category)] public async Task Valid_token_with_valid_multiple_scopes() { var tokenResponse = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client1", ClientSecret = "secret", Scope = "api1 api2", }); var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api1", ClientSecret = "secret", Token = tokenResponse.AccessToken }); introspectionResponse.IsActive.Should().Be(true); introspectionResponse.IsError.Should().Be(false); var scopes = from c in introspectionResponse.Claims where c.Type == "scope" select c; scopes.Count().Should().Be(1); scopes.First().Value.Should().Be("api1"); } [Fact] [Trait("Category", Category)] public async Task Valid_token_with_invalid_scopes_should_fail() { var tokenResponse = await _client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest { Address = TokenEndpoint, ClientId = "client1", ClientSecret = "secret", Scope = "api1", }); var introspectionResponse = await _client.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IntrospectionEndpoint, ClientId = "api2", ClientSecret = "secret", Token = tokenResponse.AccessToken }); introspectionResponse.IsActive.Should().Be(false); introspectionResponse.IsError.Should().Be(false); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/Setup/Clients.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using IdentityServer8.Models; namespace IdentityServer.IntegrationTests.Endpoints.Introspection.Setup; internal class Clients { public static IEnumerable Get() { return new List { new Client { ClientId = "client1", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "api1", "api2", "api3-a", "api3-b" }, AccessTokenType = AccessTokenType.Reference }, new Client { ClientId = "client2", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "api1", "api2", "api3-a", "api3-b" }, AccessTokenType = AccessTokenType.Reference }, new Client { ClientId = "client3", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "api1", "api2", "api3-a", "api3-b" }, AccessTokenType = AccessTokenType.Reference }, new Client { ClientId = "ro.client", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "api1", "api2", "api3-a", "api3-b" }, AccessTokenType = AccessTokenType.Reference } }; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/Setup/Scopes.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using IdentityServer8.Models; namespace IdentityServer.IntegrationTests.Endpoints.Introspection.Setup; internal class Scopes { public static IEnumerable GetApis() { return new ApiResource[] { new ApiResource { Name = "api1", ApiSecrets = new List { new Secret("secret".Sha256()) }, Scopes = { "api1" } }, new ApiResource { Name = "api2", ApiSecrets = new List { new Secret("secret".Sha256()) }, Scopes = { "api2" } }, new ApiResource { Name = "api3", ApiSecrets = new List { new Secret("secret".Sha256()) }, Scopes = { "api3-a", "api3-b" } } }; } public static IEnumerable GetScopes() { return new ApiScope[] { new ApiScope { Name = "api1" }, new ApiScope { Name = "api2" }, new ApiScope { Name = "api3-a" }, new ApiScope { Name = "api3-b" } }; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/Setup/Startup.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.Builder; using Microsoft.Extensions.DependencyInjection; using Microsoft.Extensions.Logging; namespace IdentityServer.IntegrationTests.Endpoints.Introspection.Setup; public class Startup { public void ConfigureServices(IServiceCollection services) { var builder = services.AddIdentityServer(options => { options.IssuerUri = "https://idsvr8"; options.Endpoints.EnableAuthorizeEndpoint = false; }); builder.AddInMemoryClients(Clients.Get()); builder.AddInMemoryApiResources(Scopes.GetApis()); builder.AddInMemoryApiScopes(Scopes.GetScopes()); builder.AddTestUsers(Users.Get()); builder.AddDeveloperSigningCredential(persistKey: false); } public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory) { app.UseIdentityServer(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Introspection/Setup/Users.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using IdentityServer8.Test; namespace IdentityServer.IntegrationTests.Endpoints.Introspection.Setup; public static class Users { public static List Get() { return new List { new TestUser { SubjectId = "1", Username = "bob", Password = "bob" } }; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Revocation/RevocationTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.Revocation; public class RevocationTests { private const string Category = "RevocationTests endpoint"; private string client_id = "client"; private string client_secret = "secret"; private string redirect_uri = "https://client/callback"; private string scope_name = "api"; private string scope_secret = "api_secret"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public RevocationTests() { _mockPipeline.Clients.Add(new Client { ClientId = client_id, ClientSecrets = new List { new Secret(client_secret.Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequireConsent = false, RequirePkce = false, AllowOfflineAccess = true, AllowedScopes = new List { "api" }, RedirectUris = new List { redirect_uri }, AllowAccessTokensViaBrowser = true, AccessTokenType = AccessTokenType.Reference, RefreshTokenUsage = TokenUsage.ReUse }); _mockPipeline.Clients.Add(new Client { ClientId = "implicit", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "api" }, RedirectUris = new List { redirect_uri }, AllowAccessTokensViaBrowser = true, AccessTokenType = AccessTokenType.Reference }); _mockPipeline.Clients.Add(new Client { ClientId = "implicit_and_client_creds", AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, RequireConsent = false, AllowedScopes = new List { "api" }, RedirectUris = new List { redirect_uri }, AllowAccessTokensViaBrowser = true, AccessTokenType = AccessTokenType.Reference }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId() }); _mockPipeline.ApiResources.AddRange(new ApiResource[] { new ApiResource { Name = "api", ApiSecrets = new List { new Secret(scope_secret.Sha256()) }, Scopes = { scope_name } } }); _mockPipeline.ApiScopes.AddRange(new ApiScope[] { new ApiScope { Name = scope_name } }); _mockPipeline.Initialize(); } private class Tokens { public Tokens(TokenResponse response) { AccessToken = response.AccessToken; RefreshToken = response.RefreshToken; } public string AccessToken { get; set; } public string RefreshToken { get; set; } } private async Task GetTokensAsync() { await _mockPipeline.LoginAsync("bob"); var authorizationResponse = await _mockPipeline.RequestAuthorizationEndpointAsync( client_id, "code", "api offline_access", "https://client/callback"); authorizationResponse.IsError.Should().BeFalse(); authorizationResponse.Code.Should().NotBeNull(); var tokenResponse = await _mockPipeline.BackChannelClient.RequestAuthorizationCodeTokenAsync(new AuthorizationCodeTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = client_id, ClientSecret = client_secret, Code = authorizationResponse.Code, RedirectUri = redirect_uri }); tokenResponse.IsError.Should().BeFalse(); tokenResponse.AccessToken.Should().NotBeNull(); tokenResponse.RefreshToken.Should().NotBeNull(); return new Tokens(tokenResponse); } private async Task GetAccessTokenForImplicitClientAsync(string clientId) { await _mockPipeline.LoginAsync("bob"); var authorizationResponse = await _mockPipeline.RequestAuthorizationEndpointAsync( clientId, "token", "api", "https://client/callback"); authorizationResponse.IsError.Should().BeFalse(); authorizationResponse.AccessToken.Should().NotBeNull(); return authorizationResponse.AccessToken; } private Task IsAccessTokenValidAsync(Tokens tokens) { return IsAccessTokenValidAsync(tokens.AccessToken); } private async Task IsAccessTokenValidAsync(string token) { var response = await _mockPipeline.BackChannelClient.IntrospectTokenAsync(new TokenIntrospectionRequest { Address = IdentityServerPipeline.IntrospectionEndpoint, ClientId = scope_name, ClientSecret = scope_secret, Token = token, TokenTypeHint = IdentityModel.OidcConstants.TokenTypes.AccessToken }); return response.IsError == false && response.IsActive; } private async Task UseRefreshTokenAsync(Tokens tokens) { var response = await _mockPipeline.BackChannelClient.RequestRefreshTokenAsync(new RefreshTokenRequest { Address = IdentityServerPipeline.TokenEndpoint, ClientId = client_id, ClientSecret = client_secret, RefreshToken = tokens.RefreshToken }); if (response.IsError) { return false; } tokens.AccessToken = response.AccessToken; return true; } [Fact] [Trait("Category", Category)] public async Task Get_request_should_return_405() { var response = await _mockPipeline.BackChannelClient.GetAsync(IdentityServerPipeline.RevocationEndpoint); response.StatusCode.Should().Be(HttpStatusCode.MethodNotAllowed); } [Fact] [Trait("Category", Category)] public async Task Post_without_form_urlencoded_should_return_415() { var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, null); response.StatusCode.Should().Be(HttpStatusCode.UnsupportedMediaType); } [Fact] [Trait("Category", Category)] public async Task Revoke_valid_access_token_should_return_success() { var tokens = await GetTokensAsync(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); var result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = client_id, ClientSecret = client_secret, Token = tokens.AccessToken }); result.IsError.Should().BeFalse(); (await IsAccessTokenValidAsync(tokens)).Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Revoke_valid_access_token_belonging_to_another_client_should_return_success_but_not_revoke_token() { var tokens = await GetTokensAsync(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); var result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = "implicit", ClientSecret = client_secret, Token = tokens.AccessToken }); result.IsError.Should().BeFalse(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Revoke_valid_refresh_token_should_return_success() { var tokens = await GetTokensAsync(); (await UseRefreshTokenAsync(tokens)).Should().BeTrue(); var result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = client_id, ClientSecret = client_secret, Token = tokens.RefreshToken }); result.IsError.Should().BeFalse(); (await UseRefreshTokenAsync(tokens)).Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Revoke_valid_refresh_token_belonging_to_another_client_should_return_success_but_not_revoke_token() { var tokens = await GetTokensAsync(); (await UseRefreshTokenAsync(tokens)).Should().BeTrue(); var result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = "implicit", ClientSecret = client_secret, Token = tokens.RefreshToken }); result.IsError.Should().BeFalse(); (await UseRefreshTokenAsync(tokens)).Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Revoke_invalid_access_token_should_return_success() { var tokens = await GetTokensAsync(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); var result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = client_id, ClientSecret = client_secret, Token = tokens.AccessToken }); result.IsError.Should().BeFalse(); (await IsAccessTokenValidAsync(tokens)).Should().BeFalse(); result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = client_id, ClientSecret = client_secret, Token = tokens.AccessToken }); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Revoke_invalid_refresh_token_should_return_success() { var tokens = await GetTokensAsync(); (await UseRefreshTokenAsync(tokens)).Should().BeTrue(); var result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = client_id, ClientSecret = client_secret, Token = tokens.RefreshToken }); result.IsError.Should().BeFalse(); (await UseRefreshTokenAsync(tokens)).Should().BeFalse(); result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = client_id, ClientSecret = client_secret, Token = tokens.RefreshToken }); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Invalid_client_id_should_return_error() { var tokens = await GetTokensAsync(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); var result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = "not_valid", ClientSecret = client_secret, Token = tokens.AccessToken }); result.IsError.Should().BeTrue(); result.Error.Should().Be("invalid_client"); } [Fact] [Trait("Category", Category)] public async Task Invalid_credentials_should_return_error() { var tokens = await GetTokensAsync(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); var result = await _mockPipeline.BackChannelClient.RevokeTokenAsync(new TokenRevocationRequest { Address = IdentityServerPipeline.RevocationEndpoint, ClientId = client_id, ClientSecret = "not_valid", Token = tokens.AccessToken }); result.IsError.Should().BeTrue(); result.Error.Should().Be("invalid_client"); } [Fact] [Trait("Category", Category)] public async Task Missing_token_should_return_error() { var data = new Dictionary { { "client_id", client_id }, { "client_secret", client_secret } }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, new FormUrlEncodedContent(data)); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var result = await ProtocolResponse.FromHttpResponseAsync(response); result.IsError.Should().BeTrue(); result.Error.Should().Be("invalid_request"); } [Fact] [Trait("Category", Category)] public async Task Invalid_token_type_hint_should_return_error() { var tokens = await GetTokensAsync(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); var data = new Dictionary { { "client_id", client_id }, { "client_secret", client_secret }, { "token", tokens.AccessToken }, { "token_type_hint", "not_valid" } }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, new FormUrlEncodedContent(data)); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); var result = await ProtocolResponse.FromHttpResponseAsync(response); result.IsError.Should().BeTrue(); result.Error.Should().Be("unsupported_token_type"); } [Fact] [Trait("Category", Category)] public async Task Valid_access_token_but_missing_token_type_hint_should_succeed() { var tokens = await GetTokensAsync(); (await IsAccessTokenValidAsync(tokens)).Should().BeTrue(); var data = new Dictionary { { "client_id", client_id }, { "client_secret", client_secret }, { "token", tokens.AccessToken } }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, new FormUrlEncodedContent(data)); response.StatusCode.Should().Be(HttpStatusCode.OK); (await IsAccessTokenValidAsync(tokens)).Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_refresh_token_but_missing_token_type_hint_should_succeed() { var tokens = await GetTokensAsync(); (await UseRefreshTokenAsync(tokens)).Should().BeTrue(); var data = new Dictionary { { "client_id", client_id }, { "client_secret", client_secret }, { "token", tokens.RefreshToken } }; var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, new FormUrlEncodedContent(data)); response.StatusCode.Should().Be(HttpStatusCode.OK); (await UseRefreshTokenAsync(tokens)).Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Implicit_client_without_secret_revoking_token_should_succeed() { var token = await GetAccessTokenForImplicitClientAsync("implicit"); var data = new Dictionary { { "client_id", "implicit" }, { "token", token } }; (await IsAccessTokenValidAsync(token)).Should().BeTrue(); var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, new FormUrlEncodedContent(data)); response.StatusCode.Should().Be(HttpStatusCode.OK); (await IsAccessTokenValidAsync(token)).Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Implicit_and_client_creds_client_without_secret_revoking_token_should_fail() { var token = await GetAccessTokenForImplicitClientAsync("implicit_and_client_creds"); var data = new Dictionary { { "client_id", "implicit_and_client_creds" }, { "token", token } }; (await IsAccessTokenValidAsync(token)).Should().BeTrue(); var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.RevocationEndpoint, new FormUrlEncodedContent(data)); response.StatusCode.Should().Be(HttpStatusCode.BadRequest); (await IsAccessTokenValidAsync(token)).Should().BeTrue(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Endpoints/Token/TokenEndpointTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer8.Models; using IdentityServer8.Test; using Newtonsoft.Json.Linq; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer.IntegrationTests.Common; using Xunit; namespace IdentityServer.IntegrationTests.Endpoints.Token; public class TokenEndpointTests { private const string Category = "Token endpoint"; private string client_id = "client"; private string client_secret = "secret"; private string scope_name = "api"; private string scope_secret = "api_secret"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public TokenEndpointTests() { _mockPipeline.Clients.Add(new Client { ClientId = client_id, ClientSecrets = new List { new Secret(client_secret.Sha256()) }, AllowedGrantTypes = { GrantType.ClientCredentials, GrantType.ResourceOwnerPassword }, AllowedScopes = new List { "api" }, }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Password = "password", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId() }); _mockPipeline.ApiResources.AddRange(new ApiResource[] { new ApiResource { Name = "api", ApiSecrets = new List { new Secret(scope_secret.Sha256()) }, Scopes = {scope_name} } }); _mockPipeline.ApiScopes.AddRange(new[] { new ApiScope { Name = scope_name } }); _mockPipeline.Initialize(); } [Fact] [Trait("Category", Category)] public async Task client_credentials_request_with_funny_headers_should_not_hang() { var data = new Dictionary { { "grant_type", "client_credentials" }, { "client_id", client_id }, { "client_secret", client_secret }, { "scope", scope_name }, }; var form = new FormUrlEncodedContent(data); _mockPipeline.BackChannelClient.DefaultRequestHeaders.Add("Referer", "http://127.0.0.1:33086/appservice/appservice?t=1564165664142?load"); var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.TokenEndpoint, form); response.StatusCode.Should().Be(HttpStatusCode.OK); var json = await response.Content.ReadAsStringAsync(); var result = JObject.Parse(json); result.ContainsKey("error").Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task resource_owner_request_with_funny_headers_should_not_hang() { var data = new Dictionary { { "grant_type", "password" }, { "username", "bob" }, { "password", "password" }, { "client_id", client_id }, { "client_secret", client_secret }, { "scope", scope_name }, }; var form = new FormUrlEncodedContent(data); _mockPipeline.BackChannelClient.DefaultRequestHeaders.Add("Referer", "http://127.0.0.1:33086/appservice/appservice?t=1564165664142?load"); var response = await _mockPipeline.BackChannelClient.PostAsync(IdentityServerPipeline.TokenEndpoint, form); response.StatusCode.Should().Be(HttpStatusCode.OK); var json = await response.Content.ReadAsStringAsync(); var result = JObject.Parse(json); result.ContainsKey("error").Should().BeFalse(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Extensibility/CustomProfileServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Net; using System.Security.Claims; using System.Text; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Services; using Microsoft.Extensions.DependencyInjection; using Newtonsoft.Json.Linq; using Xunit; namespace IdentityServer.IntegrationTests.Extensibility; public class CustomProfileServiceTests { private const string Category = "Authorize endpoint"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); public CustomProfileServiceTests() { _mockPipeline.OnPostConfigureServices += svcs => { svcs.AddTransient(); }; _mockPipeline.Clients.Add(new Client { ClientId = "implicit", AllowedGrantTypes = GrantTypes.Implicit, RedirectUris = { "https://client/callback" }, RequireConsent = false, AllowedScopes = { "openid", "custom_identity" } }); _mockPipeline.IdentityScopes.Add(new IdentityResources.OpenId()); _mockPipeline.IdentityScopes.Add(new IdentityResource("custom_identity", new string[] { "foo" })); _mockPipeline.Users.Add(new IdentityServer8.Test.TestUser { SubjectId = "bob", Username = "bob", Password = "password", }); _mockPipeline.Initialize(); } [Fact] public async Task custom_profile_should_return_claims_for_implicit_client() { await _mockPipeline.LoginAsync("bob"); var url = _mockPipeline.CreateAuthorizeUrl( clientId: "implicit", responseType: "id_token", scope: "openid custom_identity", redirectUri: "https://client/callback", state: "state", nonce: "nonce"); _mockPipeline.BrowserClient.AllowAutoRedirect = false; var response = await _mockPipeline.BrowserClient.GetAsync(url); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Headers.Location.ToString().Should().StartWith("https://client/callback"); var authorization = new IdentityModel.Client.AuthorizeResponse(response.Headers.Location.ToString()); authorization.IsError.Should().BeFalse(); authorization.IdentityToken.Should().NotBeNull(); var payload = authorization.IdentityToken.Split('.')[1]; var json = Encoding.UTF8.GetString(Base64Url.Decode(payload)); var obj = JObject.Parse(json); obj.GetValue("foo").Should().NotBeNull(); obj["foo"].ToString().Should().Be("bar"); } } public class CustomProfileService : IProfileService { public Task GetProfileDataAsync(ProfileDataRequestContext context) { var claims = new Claim[] { new Claim("foo", "bar") }; context.AddRequestedClaims(claims); return Task.CompletedTask; } public Task IsActiveAsync(IsActiveContext context) { context.IsActive = true; return Task.CompletedTask; } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/IdentityServer.IntegrationTests.csproj ================================================ ../../../../key.snk true true runtime; build; native; contentfiles; analyzers; buildtransitive all Always Always PreserveNewest ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Pipeline/CorsTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Test; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer.IntegrationTests.Pipeline; public class CorsTests { private const string Category = "CORS Integration"; private IdentityServerPipeline _pipeline = new IdentityServerPipeline(); public CorsTests() { _pipeline.Clients.AddRange(new Client[] { new Client { ClientId = "client", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = true, AllowedScopes = new List { "openid", "profile", "api1", "api2" }, RedirectUris = new List { "https://client/callback" }, AllowedCorsOrigins = new List { "https://client" } } }); _pipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _pipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email() }); _pipeline.ApiResources.AddRange(new ApiResource[] { new ApiResource { Name = "api", Scopes = { "api1", "api2" } } }); _pipeline.ApiScopes.AddRange(new[] { new ApiScope { Name = "api1" }, new ApiScope { Name = "api2" } }); _pipeline.Initialize(); } [Theory] [InlineData(IdentityServerPipeline.DiscoveryEndpoint)] [InlineData(IdentityServerPipeline.DiscoveryKeysEndpoint)] [InlineData(IdentityServerPipeline.TokenEndpoint)] [InlineData(IdentityServerPipeline.UserInfoEndpoint)] [InlineData(IdentityServerPipeline.RevocationEndpoint)] [Trait("Category", Category)] public async Task cors_request_to_allowed_endpoints_should_succeed(string url) { _pipeline.BackChannelClient.DefaultRequestHeaders.Add("Origin", "https://client"); _pipeline.BackChannelClient.DefaultRequestHeaders.Add("Access-Control-Request-Method", "GET"); var message = new HttpRequestMessage(HttpMethod.Options, url); var response = await _pipeline.BackChannelClient.SendAsync(message); response.StatusCode.Should().Be(HttpStatusCode.NoContent); response.Headers.Contains("Access-Control-Allow-Origin").Should().BeTrue(); } [Theory] [InlineData(IdentityServerPipeline.AuthorizeEndpoint)] [InlineData(IdentityServerPipeline.EndSessionEndpoint)] [InlineData(IdentityServerPipeline.CheckSessionEndpoint)] [InlineData(IdentityServerPipeline.LoginPage)] [InlineData(IdentityServerPipeline.ConsentPage)] [InlineData(IdentityServerPipeline.ErrorPage)] [Trait("Category", Category)] public async Task cors_request_to_restricted_endpoints_should_not_succeed(string url) { _pipeline.BackChannelClient.DefaultRequestHeaders.Add("Origin", "https://client"); _pipeline.BackChannelClient.DefaultRequestHeaders.Add("Access-Control-Request-Method", "GET"); var message = new HttpRequestMessage(HttpMethod.Options, url); var response = await _pipeline.BackChannelClient.SendAsync(message); response.Headers.Contains("Access-Control-Allow-Origin").Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task custom_cors_policy_provider_should_be_used() { var policy = new StubCorePolicyProvider(); _pipeline.OnPreConfigureServices += services => { services.AddSingleton(policy); }; _pipeline.Initialize(); _pipeline.BackChannelClient.DefaultRequestHeaders.Add("Origin", "https://client"); _pipeline.BackChannelClient.DefaultRequestHeaders.Add("Access-Control-Request-Method", "GET"); var message = new HttpRequestMessage(HttpMethod.Options, IdentityServerPipeline.DiscoveryEndpoint); var response = await _pipeline.BackChannelClient.SendAsync(message); policy.WasCalled.Should().BeTrue(); } } public class StubCorePolicyProvider : ICorsPolicyService { public bool Result; public bool WasCalled; public Task IsOriginAllowedAsync(string origin) { WasCalled = true; return Task.FromResult(Result); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Pipeline/FederatedSignoutTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Net; using System.Net.Http; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.IntegrationTests.Common; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Test; using Microsoft.AspNetCore.Authentication; using Xunit; namespace IdentityServer.IntegrationTests.Pipeline; public class FederatedSignoutTests { private const string Category = "Federated Signout"; private IdentityServerPipeline _pipeline = new IdentityServerPipeline(); private ClaimsPrincipal _user; public FederatedSignoutTests() { _user = new IdentityServerUser("bob") { AdditionalClaims = { new Claim(JwtClaimTypes.SessionId, "123") } }.CreatePrincipal(); _pipeline = new IdentityServerPipeline(); _pipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId() }); _pipeline.Clients.Add(new Client { ClientId = "client1", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://client1/callback" }, FrontChannelLogoutUri = "https://client1/signout", PostLogoutRedirectUris = new List { "https://client1/signout-callback" }, AllowAccessTokensViaBrowser = true }); _pipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _pipeline.Initialize(); } [Fact] public async Task valid_request_to_federated_signout_endpoint_should_render_page_with_iframe() { await _pipeline.LoginAsync(_user); await _pipeline.RequestAuthorizationEndpointAsync( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _pipeline.BrowserClient.GetAsync(IdentityServerPipeline.FederatedSignOutUrl + "?sid=123"); response.StatusCode.Should().Be(HttpStatusCode.OK); response.Content.Headers.ContentType.MediaType.Should().Be("text/html"); var html = await response.Content.ReadAsStringAsync(); html.Should().Contain("https://server/connect/endsession/callback?endSessionId="); } [Fact] public async Task valid_POST_request_to_federated_signout_endpoint_should_render_page_with_iframe() { await _pipeline.LoginAsync(_user); await _pipeline.RequestAuthorizationEndpointAsync( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _pipeline.BrowserClient.PostAsync(IdentityServerPipeline.FederatedSignOutUrl, new FormUrlEncodedContent(new Dictionary { { "sid", "123" } })); response.StatusCode.Should().Be(HttpStatusCode.OK); response.Content.Headers.ContentType.MediaType.Should().Be("text/html"); var html = await response.Content.ReadAsStringAsync(); html.Should().Contain("https://server/connect/endsession/callback?endSessionId="); } [Fact] public async Task no_clients_signed_into_should_not_render_page_with_iframe() { await _pipeline.LoginAsync(_user); var response = await _pipeline.BrowserClient.GetAsync(IdentityServerPipeline.FederatedSignOutUrl + "?sid=123"); response.StatusCode.Should().Be(HttpStatusCode.OK); response.Content.Headers.ContentType.Should().BeNull(); var html = await response.Content.ReadAsStringAsync(); html.Should().Be(String.Empty); } [Fact] public async Task no_authenticated_user_should_not_render_page_with_iframe() { var response = await _pipeline.BrowserClient.GetAsync(IdentityServerPipeline.FederatedSignOutUrl + "?sid=123"); response.StatusCode.Should().Be(HttpStatusCode.OK); response.Content.Headers.ContentType.Should().BeNull(); var html = await response.Content.ReadAsStringAsync(); html.Should().Be(String.Empty); } [Fact] public async Task user_not_signed_out_should_not_render_page_with_iframe() { _pipeline.OnFederatedSignout = ctx => { return Task.FromResult(true); }; await _pipeline.LoginAsync(_user); await _pipeline.RequestAuthorizationEndpointAsync( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _pipeline.BrowserClient.GetAsync(IdentityServerPipeline.FederatedSignOutUrl + "?sid=123"); response.StatusCode.Should().Be(HttpStatusCode.OK); response.Content.Headers.ContentType.Should().BeNull(); var html = await response.Content.ReadAsStringAsync(); html.Should().Be(String.Empty); } [Fact] public async Task non_200_should_not_render_page_with_iframe() { _pipeline.OnFederatedSignout = async ctx => { await ctx.SignOutAsync(); // even if we signout, we should not see iframes ctx.Response.Redirect("http://foo"); return true; }; await _pipeline.LoginAsync(_user); await _pipeline.RequestAuthorizationEndpointAsync( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); _pipeline.BrowserClient.AllowAutoRedirect = false; var response = await _pipeline.BrowserClient.GetAsync(IdentityServerPipeline.FederatedSignOutUrl + "?sid=123"); response.StatusCode.Should().Be(HttpStatusCode.Redirect); response.Content.Headers.ContentType.Should().BeNull(); var html = await response.Content.ReadAsStringAsync(); html.Should().Be(String.Empty); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/Pipeline/SubpathHosting.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel.Client; using IdentityServer.IntegrationTests.Common; using IdentityServer8.Models; using IdentityServer8.Test; using Xunit; namespace IdentityServer.IntegrationTests.Pipeline; public class SubpathHosting { private const string Category = "Subpath endpoint"; private IdentityServerPipeline _mockPipeline = new IdentityServerPipeline(); private Client _client1; public SubpathHosting() { _mockPipeline.Clients.AddRange(new Client[] { _client1 = new Client { ClientId = "client1", AllowedGrantTypes = GrantTypes.Implicit, RequireConsent = false, AllowedScopes = new List { "openid", "profile" }, RedirectUris = new List { "https://client1/callback" }, AllowAccessTokensViaBrowser = true } }); _mockPipeline.Users.Add(new TestUser { SubjectId = "bob", Username = "bob", Claims = new Claim[] { new Claim("name", "Bob Loblaw"), new Claim("email", "bob@loblaw.com"), new Claim("role", "Attorney") } }); _mockPipeline.IdentityScopes.AddRange(new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email() }); _mockPipeline.Initialize("/subpath"); } [Fact] [Trait("Category", Category)] public async Task anonymous_user_should_be_redirected_to_login_page() { var url = new RequestUrl("https://server/subpath/connect/authorize").CreateAuthorizeUrl( clientId: "client1", responseType: "id_token", scope: "openid", redirectUri: "https://client1/callback", state: "123_state", nonce: "123_nonce"); var response = await _mockPipeline.BrowserClient.GetAsync(url); _mockPipeline.LoginWasCalled.Should().BeTrue(); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/identityserver_testing.cer ================================================ -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgIQO+9qzaJY9I5Ewq81kNEKWTANBgkqhkiG9w0BAQsFADAh MR8wHQYDVQQDDBZpZGVudGl0eXNlcnZlcl90ZXN0aW5nMCAXDTIwMDEyMjIzMTYz OFoYDzIxMDMwNTIyMjMyNjM5WjAhMR8wHQYDVQQDDBZpZGVudGl0eXNlcnZlcl90 ZXN0aW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuyZQvE+6gEoB a+LV3dxjk/Xj4DRJ/tefQKR2AZoYAAfKN4ditFmEfRC1A6rckBpMVihTeb1kwwQT 7H4HTS6O/ERHVjOdghoOjEsVakWhAkvh8gphC4IU0upXlYqMh2WzgXEXwYRFB9Tk 7zoHb1/zjEEAhCf2Xbi6YslBoU71bFWyAaeOTl859wV6WaiBIK5L8nJUaIaq4zmC k8caPZq5E867mZiMdL4TcW9/YoAAO96Wa/W9o6OiuZrP414TlEjVuccpLXvjk0hB U5OZD2bTvD3MQZu1n1QMLwXfaOBrcv1/RqYJkK7vpP6Pp1YYlo7b2PBDVAIrRSVT laViBP0owQIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB BQUHAwIGCCsGAQUFBwMBMCEGA1UdEQQaMBiCFmlkZW50aXR5c2VydmVyX3Rlc3Rp bmcwHQYDVR0OBBYEFCVXNpKp8QlHywXEBFfpXxWcOMcsMA0GCSqGSIb3DQEBCwUA A4IBAQBsEAzwyN6V6N5ggN9G1O0ZpviSjixGkWtySNCBjbGXAhOvfW4M3ysNkDwZ ltk/Q17ihZzw135MrDCmnr5pRiN4CbEGbe1qsb+Z0uCCn8/WcIVYYooW66H/Jez+ dg5RxUukA67ZDnjzRskIer7L2t1C4pDqpvPVcneUxkiYDSgcKpTuCVjkPNQKQTIw Sm98NkQG8G8V8+ENIU837ytkiC5nqQa4zDRHexzWrYhiuayWWxJKcNRVF9YaE8ts vp5N1ewmWbSgF8caJuKraVOISj9R4iqf0XuhfSpW/7eIWYmXfqy/UloeqlALfP5C 2d2FdDSfsQ4Jgc3ebrECAQaCC3Gq -----END CERTIFICATE----- ================================================ FILE: src/IdentityServer8/test/IdentityServer.IntegrationTests/xunit.runner.json ================================================ { "methodDisplay":"classAndMethod" } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/IAuthenticationSchemeHandler.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer.UnitTests.Common { internal interface IAuthenticationSchemeHandler { } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockAuthenticationHandler.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; namespace IdentityServer.UnitTests.Common { internal class MockAuthenticationHandler : IAuthenticationHandler { public AuthenticateResult Result { get; set; } = AuthenticateResult.NoResult(); public Task AuthenticateAsync() { return Task.FromResult(Result); } public Task ChallengeAsync(AuthenticationProperties properties) { return Task.CompletedTask; } public Task ForbidAsync(AuthenticationProperties properties) { return Task.CompletedTask; } public Task InitializeAsync(AuthenticationScheme scheme, HttpContext context) { return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockAuthenticationHandlerProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; namespace IdentityServer.UnitTests.Common { internal class MockAuthenticationHandlerProvider : IAuthenticationHandlerProvider { public IAuthenticationHandler Handler { get; set; } public Task GetHandlerAsync(HttpContext context, string authenticationScheme) { return Task.FromResult(Handler); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockAuthenticationSchemeProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; namespace IdentityServer.UnitTests.Common { internal class MockAuthenticationSchemeProvider : IAuthenticationSchemeProvider { public string Default { get; set; } = "scheme"; public List Schemes { get; set; } = new List() { new AuthenticationScheme("scheme", null, typeof(MockAuthenticationHandler)) }; public void AddScheme(AuthenticationScheme scheme) { Schemes.Add(scheme); } public Task> GetAllSchemesAsync() { return Task.FromResult(Schemes.AsEnumerable()); } public Task GetDefaultAuthenticateSchemeAsync() { var scheme = Schemes.Where(x => x.Name == Default).FirstOrDefault(); return Task.FromResult(scheme); } public Task GetDefaultChallengeSchemeAsync() { return GetDefaultAuthenticateSchemeAsync(); } public Task GetDefaultForbidSchemeAsync() { return GetDefaultAuthenticateSchemeAsync(); } public Task GetDefaultSignInSchemeAsync() { return GetDefaultAuthenticateSchemeAsync(); } public Task GetDefaultSignOutSchemeAsync() { return GetDefaultAuthenticateSchemeAsync(); } public Task> GetRequestHandlerSchemesAsync() { return Task.FromResult(Schemes.AsEnumerable()); } public Task GetSchemeAsync(string name) { return Task.FromResult(Schemes.Where(x => x.Name == name).FirstOrDefault()); } public void RemoveScheme(string name) { var scheme = Schemes.Where(x => x.Name == name).FirstOrDefault(); if (scheme != null) { Schemes.Remove(scheme); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockAuthenticationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Security.Claims; using System.Threading.Tasks; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; namespace IdentityServer.UnitTests.Common { internal class MockAuthenticationService : IAuthenticationService { public AuthenticateResult Result { get; set; } public Task AuthenticateAsync(HttpContext context, string scheme) { return Task.FromResult(Result); } public Task ChallengeAsync(HttpContext context, string scheme, AuthenticationProperties properties) { return Task.CompletedTask; } public Task ForbidAsync(HttpContext context, string scheme, AuthenticationProperties properties) { return Task.CompletedTask; } public Task SignInAsync(HttpContext context, string scheme, ClaimsPrincipal principal, AuthenticationProperties properties) { return Task.CompletedTask; } public Task SignOutAsync(HttpContext context, string scheme, AuthenticationProperties properties) { return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockClaimsService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Services; using IdentityServer8.Validation; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; namespace IdentityServer.UnitTests.Common { class MockClaimsService : IClaimsService { public List IdentityTokenClaims { get; set; } = new List(); public List AccessTokenClaims { get; set; } = new List(); public Task> GetIdentityTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resources, bool includeAllIdentityClaims, ValidatedRequest request) { return Task.FromResult(IdentityTokenClaims.AsEnumerable()); } public Task> GetAccessTokenClaimsAsync(ClaimsPrincipal subject, ResourceValidationResult resources, ValidatedRequest request) { return Task.FromResult(AccessTokenClaims.AsEnumerable()); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockClientSessionService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer.UnitTests.Common { //class MockClientSessionService : IClientSessionService //{ // public List Clients = new List(); // public bool RemoveCookieWasCalled { get; private set; } // public Task AddClientIdAsync(string clientId) // { // Clients.Add(clientId); // return Task.CompletedTask; // } // public Task EnsureClientListCookieAsync(string sid) // { // return Task.CompletedTask; // } // public Task> GetClientListAsync() // { // return Task.FromResult>(Clients); // } // public IEnumerable GetClientListFromCookie(string sid) // { // return Clients; // } // public void RemoveCookie(string sid) // { // RemoveCookieWasCalled = true; // Clients.Clear(); // } //} } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockConsentMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Stores; namespace IdentityServer.UnitTests.Common { public class MockConsentMessageStore : IConsentMessageStore { public Dictionary> Messages { get; set; } = new Dictionary>(); public Task DeleteAsync(string id) { if (id != null && Messages.ContainsKey(id)) { Messages.Remove(id); } return Task.CompletedTask; } public Task> ReadAsync(string id) { Message val = null; if (id != null) { Messages.TryGetValue(id, out val); } return Task.FromResult(val); } public Task WriteAsync(string id, Message message) { Messages[id] = message; return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockConsentService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Common { public class MockConsentService : IConsentService { public bool RequiresConsentResult { get; set; } public Task RequiresConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable parsedScopes) { return Task.FromResult(RequiresConsentResult); } public ClaimsPrincipal ConsentSubject { get; set; } public Client ConsentClient { get; set; } public IEnumerable ConsentScopes { get; set; } public Task UpdateConsentAsync(ClaimsPrincipal subject, Client client, IEnumerable parsedScopes) { ConsentSubject = subject; ConsentClient = client; ConsentScopes = parsedScopes?.Select(x => x.RawValue); return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockHttpContextAccessor.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Stores; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; namespace IdentityServer.UnitTests.Common { internal class MockHttpContextAccessor : IHttpContextAccessor { private HttpContext _context = new DefaultHttpContext(); public MockAuthenticationService AuthenticationService { get; set; } = new MockAuthenticationService(); public MockAuthenticationSchemeProvider Schemes { get; set; } = new MockAuthenticationSchemeProvider(); public MockHttpContextAccessor( IdentityServerOptions options = null, IUserSession userSession = null, IMessageStore endSessionStore = null) { options = options ?? TestIdentityServerOptions.Create(); var services = new ServiceCollection(); services.AddSingleton(options); services.AddSingleton(Schemes); services.AddSingleton(AuthenticationService); services.AddAuthentication(auth => { auth.DefaultAuthenticateScheme = Schemes.Default; }); if (userSession == null) { services.AddScoped(); } else { services.AddSingleton(userSession); } if (endSessionStore == null) { services.AddTransient, ProtectedDataMessageStore>(); } else { services.AddSingleton(endSessionStore); } _context.RequestServices = services.BuildServiceProvider(); } public HttpContext HttpContext { get { return _context; } set { _context = value; } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockKeyMaterialService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Services; using Microsoft.IdentityModel.Tokens; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IdentityServer.UnitTests.Common { class MockKeyMaterialService : IKeyMaterialService { public List SigningCredentials = new List(); public List ValidationKeys = new List(); public Task> GetAllSigningCredentialsAsync() { return Task.FromResult(SigningCredentials.AsEnumerable()); } public Task GetSigningCredentialsAsync(IEnumerable allowedAlgorithms = null) { return Task.FromResult(SigningCredentials.FirstOrDefault()); } public Task> GetValidationKeysAsync() { return Task.FromResult(ValidationKeys.AsEnumerable()); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockLogoutNotificationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Services; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IdentityServer.UnitTests.Common { public class MockLogoutNotificationService : ILogoutNotificationService { public bool GetFrontChannelLogoutNotificationsUrlsCalled { get; set; } public List FrontChannelLogoutNotificationsUrls { get; set; } = new List(); public bool SendBackChannelLogoutNotificationsCalled { get; set; } public List BackChannelLogoutRequests { get; set; } = new List(); public Task> GetFrontChannelLogoutNotificationsUrlsAsync(LogoutNotificationContext context) { GetFrontChannelLogoutNotificationsUrlsCalled = true; return Task.FromResult(FrontChannelLogoutNotificationsUrls.AsEnumerable()); } public Task> GetBackChannelLogoutNotificationsAsync(LogoutNotificationContext context) { SendBackChannelLogoutNotificationsCalled = true; return Task.FromResult(BackChannelLogoutRequests.AsEnumerable()); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockMessageStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Stores; namespace IdentityServer.UnitTests.Common { public class MockMessageStore : IMessageStore { public Dictionary> Messages { get; set; } = new Dictionary>(); public Task> ReadAsync(string id) { Message val = null; if (id != null) { Messages.TryGetValue(id, out val); } return Task.FromResult(val); } public Task WriteAsync(Message message) { var id = Guid.NewGuid().ToString(); Messages[id] = message; return Task.FromResult(id); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockPersistedGrantService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Common { public class MockPersistedGrantService : IPersistedGrantService { public IEnumerable GetAllGrantsResult { get; set; } public bool RemoveAllGrantsWasCalled { get; set; } public Task> GetAllGrantsAsync(string subjectId) { return Task.FromResult(GetAllGrantsResult ?? Enumerable.Empty()); } public Task RemoveAllGrantsAsync(string subjectId, string clientId, string sessionId = null) { RemoveAllGrantsWasCalled = true; return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockProfileService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Common { public class MockProfileService : IProfileService { public ICollection ProfileClaims { get; set; } = new HashSet(); public bool IsActive { get; set; } = true; public bool GetProfileWasCalled => ProfileContext != null; public ProfileDataRequestContext ProfileContext { get; set; } public bool IsActiveWasCalled => ActiveContext != null; public IsActiveContext ActiveContext { get; set; } public Task GetProfileDataAsync(ProfileDataRequestContext context) { ProfileContext = context; context.IssuedClaims = ProfileClaims.ToList(); return Task.CompletedTask; } public Task IsActiveAsync(IsActiveContext context) { ActiveContext = context; context.IsActive = IsActive; return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockReferenceTokenStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Stores; using System; using System.Threading.Tasks; namespace IdentityServer.UnitTests.Common { class MockReferenceTokenStore : IReferenceTokenStore { public Task GetReferenceTokenAsync(string handle) { throw new NotImplementedException(); } public Task RemoveReferenceTokenAsync(string handle) { throw new NotImplementedException(); } public Task RemoveReferenceTokensAsync(string subjectId, string clientId) { throw new NotImplementedException(); } public Task StoreReferenceTokenAsync(Token token) { throw new NotImplementedException(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockResourceValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Validation; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace IdentityServer.UnitTests.Common { class MockResourceValidator : IResourceValidator { public ResourceValidationResult Result { get; set; } = new ResourceValidationResult(); public Task> ParseRequestedScopesAsync(IEnumerable scopeValues) { return Task.FromResult(scopeValues.Select(x => new ParsedScopeValue(x))); } public Task ValidateRequestedResourcesAsync(ResourceValidationRequest request) { return Task.FromResult(Result); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockReturnUrlParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Linq; using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Common { public class MockReturnUrlParser : ReturnUrlParser { public AuthorizationRequest AuthorizationRequestResult { get; set; } public bool IsValidReturnUrlResult { get; set; } public MockReturnUrlParser() : base(Enumerable.Empty()) { } public override Task ParseAsync(string returnUrl) { return Task.FromResult(AuthorizationRequestResult); } public override bool IsValidReturnUrl(string returnUrl) { return IsValidReturnUrlResult; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockSessionIdService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer.UnitTests.Common { //public class MockSessionIdService : ISessionIdService //{ // public bool RemoveCookieWasCalled { get; private set; } // public string SessionId { get; set; } = "session_id"; // public void CreateSessionId(SignInContext context) // { // } // public Task EnsureSessionCookieAsync() // { // return Task.CompletedTask; // } // public string GetCookieName() // { // return "sessionid"; // } // public string GetCookieValue() // { // return SessionId; // } // public Task GetCurrentSessionIdAsync() // { // return Task.FromResult(SessionId); // } // public void RemoveCookie() // { // RemoveCookieWasCalled = true; // SessionId = null; // } //} } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockSystemClock.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.AspNetCore.Authentication; using System; namespace IdentityServer.UnitTests.Common { class MockSystemClock : ISystemClock { public DateTimeOffset Now { get; set; } public DateTimeOffset UtcNow { get { return Now; } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockTokenCreationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Services; using System.Threading.Tasks; namespace IdentityServer.UnitTests.Common { class MockTokenCreationService : ITokenCreationService { public string Token { get; set; } public Task CreateTokenAsync(Token token) { return Task.FromResult(Token); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/MockUserSession.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer8.Services; using Microsoft.AspNetCore.Authentication; namespace IdentityServer.UnitTests.Common { public class MockUserSession : IUserSession { public List Clients = new List(); public bool EnsureSessionIdCookieWasCalled { get; set; } public bool RemoveSessionIdCookieWasCalled { get; set; } public bool CreateSessionIdWasCalled { get; set; } public ClaimsPrincipal User { get; set; } public string SessionId { get; set; } public AuthenticationProperties Properties { get; set; } public Task CreateSessionIdAsync(ClaimsPrincipal principal, AuthenticationProperties properties) { CreateSessionIdWasCalled = true; User = principal; SessionId = Guid.NewGuid().ToString(); return Task.FromResult(SessionId); } public Task GetUserAsync() { return Task.FromResult(User); } Task IUserSession.GetSessionIdAsync() { return Task.FromResult(SessionId); } public Task EnsureSessionIdCookieAsync() { EnsureSessionIdCookieWasCalled = true; return Task.CompletedTask; } public Task RemoveSessionIdCookieAsync() { RemoveSessionIdCookieWasCalled = true; return Task.CompletedTask; } public Task> GetClientListAsync() { return Task.FromResult>(Clients); } public Task AddClientIdAsync(string clientId) { Clients.Add(clientId); return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/NetworkHandler.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Net; using System.Net.Http; using System.Threading; using System.Threading.Tasks; namespace IdentityServer.UnitTests.Common { public class NetworkHandler : HttpMessageHandler { enum Behavior { Throw, ReturnError, ReturnDocument } private readonly Exception _exception; private readonly Behavior _behavior; private readonly HttpStatusCode _statusCode; private readonly string _reason; private readonly string _document; private readonly Func _selector; private readonly Func _action; public HttpRequestMessage Request { get; set; } public string Body { get; set; } public NetworkHandler(Exception exception) { _exception = exception; _behavior = Behavior.Throw; } public NetworkHandler(HttpStatusCode statusCode, string reason) { _statusCode = statusCode; _reason = reason; _behavior = Behavior.ReturnError; } public NetworkHandler(string document, HttpStatusCode statusCode) { _statusCode = statusCode; _document = document; _behavior = Behavior.ReturnDocument; } public NetworkHandler(Func documentSelector, HttpStatusCode statusCode) { _statusCode = statusCode; _selector = documentSelector; _behavior = Behavior.ReturnDocument; } public NetworkHandler(Func action) { _action = action; } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { Request = request; Body = await SafeReadContentFrom(request); if (_action != null) { return _action(request); } if (_behavior == Behavior.Throw) throw _exception; var response = new HttpResponseMessage(_statusCode); if (_behavior == Behavior.ReturnError) { response.ReasonPhrase = _reason; } if (_behavior == Behavior.ReturnDocument) { if (_selector != null) { response.Content = new StringContent(_selector(request)); } else { response.Content = new StringContent(_document); } } return response; } private async Task SafeReadContentFrom(HttpRequestMessage request) { if (request.Content == null) return null; return await request.Content.ReadAsStringAsync(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/StubAuthorizeResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.ResponseHandling; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Common { internal class StubAuthorizeResponseGenerator : IAuthorizeResponseGenerator { public AuthorizeResponse Response { get; set; } = new AuthorizeResponse(); public Task CreateResponseAsync(ValidatedAuthorizeRequest request) { return Task.FromResult(Response); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/StubClock.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using Microsoft.AspNetCore.Authentication; namespace IdentityServer.UnitTests.Common { internal class StubClock : ISystemClock { public Func UtcNowFunc = () => DateTime.UtcNow; public DateTimeOffset UtcNow => new DateTimeOffset(UtcNowFunc()); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/StubHandleGenerationService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Common { public class StubHandleGenerationService : DefaultHandleGenerationService, IHandleGenerationService { public string Handle { get; set; } public new Task GenerateAsync(int length) { if (Handle != null) return Task.FromResult(Handle); return base.GenerateAsync(length); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/TestCert.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IO; using System.Security.Cryptography.X509Certificates; using Microsoft.IdentityModel.Tokens; namespace IdentityServer.UnitTests.Common { internal static class TestCert { public static X509Certificate2 Load() { var cert = Path.Combine(System.AppContext.BaseDirectory, "identityserver_testing.pfx"); return new X509Certificate2(cert, "password"); } public static SigningCredentials LoadSigningCredentials() { var cert = Load(); return new SigningCredentials(new X509SecurityKey(cert), "RS256"); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/TestEventService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer8.Events; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Common { public class TestEventService : IEventService { private Dictionary _events = new Dictionary(); public Task RaiseAsync(Event evt) { _events.Add(evt.GetType(), evt); return Task.CompletedTask; } public T AssertEventWasRaised() where T : class { _events.ContainsKey(typeof(T)).Should().BeTrue(); return (T)_events.Where(x => x.Key == typeof(T)).Select(x=>x.Value).First(); } public bool CanRaiseEventType(EventTypes evtType) { return true; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/TestExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Linq; namespace IdentityServer.UnitTests.Common { internal static class TestExtensions { public static string Repeat(this string value, int count) { var parts = new string[count]; return parts.Aggregate((x, y) => (x ?? value) + value); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/TestIdentityServerOptions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Configuration; namespace IdentityServer.UnitTests.Common { internal class TestIdentityServerOptions { public static IdentityServerOptions Create() { var options = new IdentityServerOptions { IssuerUri = "https://idsvr.com" }; return options; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/TestLogger.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.Extensions.Logging; namespace IdentityServer.UnitTests.Common { public static class TestLogger { public static ILogger Create() { return new LoggerFactory().CreateLogger(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Common/TestUserConsentStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Stores; using IdentityServer8.Stores.Serialization; namespace IdentityServer.UnitTests.Common { public class TestUserConsentStore : IUserConsentStore { private DefaultUserConsentStore _userConsentStore; private InMemoryPersistedGrantStore _grantStore = new InMemoryPersistedGrantStore(); public TestUserConsentStore() { _userConsentStore = new DefaultUserConsentStore( _grantStore, new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); } public Task StoreUserConsentAsync(Consent consent) { return _userConsentStore.StoreUserConsentAsync(consent); } public Task GetUserConsentAsync(string subjectId, string clientId) { return _userConsentStore.GetUserConsentAsync(subjectId, clientId); } public Task RemoveUserConsentAsync(string subjectId, string clientId) { return _userConsentStore.RemoveUserConsentAsync(subjectId, clientId); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Cors/MockCorsPolicyProvider.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Http; namespace IdentityServer.UnitTests.Cors { public class MockCorsPolicyProvider : ICorsPolicyProvider { public bool WasCalled { get; set; } public CorsPolicy Response { get; set; } public Task GetPolicyAsync(HttpContext context, string policyName) { WasCalled = true; return Task.FromResult(Response); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Cors/MockCorsPolicyService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Cors { public class MockCorsPolicyService : ICorsPolicyService { public bool WasCalled { get; set; } public bool Response { get; set; } public Task IsOriginAllowedAsync(string origin) { WasCalled = true; return Task.FromResult(Response); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Cors/PolicyProviderTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Configuration.DependencyInjection; using IdentityServer8.Hosting; using IdentityServer8.Services; using Microsoft.AspNetCore.Cors.Infrastructure; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer.UnitTests.Cors { public class PolicyProviderTests { private const string Category = "PolicyProvider"; private CorsPolicyProvider _subject; private List _allowedPaths = new List(); private MockCorsPolicyProvider _mockInner = new MockCorsPolicyProvider(); private MockCorsPolicyService _mockPolicy = new MockCorsPolicyService(); private IdentityServerOptions _options; public PolicyProviderTests() { Init(); } internal void Init() { _options = new IdentityServerOptions(); _options.Cors.CorsPaths.Clear(); foreach(var path in _allowedPaths) { _options.Cors.CorsPaths.Add(new PathString(path)); } var ctx = new DefaultHttpContext(); var svcs = new ServiceCollection(); svcs.AddSingleton(_mockPolicy); ctx.RequestServices = svcs.BuildServiceProvider(); var ctxAccessor = new HttpContextAccessor(); ctxAccessor.HttpContext = ctx; _subject = new CorsPolicyProvider( TestLogger.Create(), new Decorator(_mockInner), _options, ctxAccessor); } [Theory] [InlineData("/foo")] [InlineData("/bar/")] [InlineData("/baz/quux")] [InlineData("/baz/quux/")] [Trait("Category", Category)] public async Task valid_paths_should_call_policy_service(string path) { _allowedPaths.AddRange(new string[] { "/foo", "/bar/", "/baz/quux", "/baz/quux/" }); Init(); var ctx = new DefaultHttpContext(); ctx.Request.Scheme = "https"; ctx.Request.Host = new HostString("server"); ctx.Request.Path = new PathString(path); ctx.Request.Headers.Append("Origin", "http://notserver"); var response = await _subject.GetPolicyAsync(ctx, _options.Cors.CorsPolicyName); _mockPolicy.WasCalled.Should().BeTrue(); _mockInner.WasCalled.Should().BeFalse(); } [Theory] [InlineData("/foo/")] [InlineData("/xoxo")] [InlineData("/xoxo/")] [InlineData("/foo/xoxo")] [InlineData("/baz/quux/xoxo")] [Trait("Category", Category)] public async Task invalid_paths_should_not_call_policy_service(string path) { _allowedPaths.AddRange(new string[] { "/foo", "/bar", "/baz/quux" }); Init(); var ctx = new DefaultHttpContext(); ctx.Request.Scheme = "https"; ctx.Request.Host = new HostString("server"); ctx.Request.Path = new PathString(path); ctx.Request.Headers.Append("Origin", "http://notserver"); var response = await _subject.GetPolicyAsync(ctx, _options.Cors.CorsPolicyName); _mockPolicy.WasCalled.Should().BeFalse(); _mockInner.WasCalled.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task different_policy_name_should_call_inner_policy_service() { _allowedPaths.AddRange(new string[] { "/foo", "/bar", "/baz/quux" }); Init(); var ctx = new DefaultHttpContext(); ctx.Request.Scheme = "https"; ctx.Request.Host = new HostString("server"); ctx.Request.Path = new PathString("/foo"); ctx.Request.Headers.Append("Origin", "http://notserver"); var response = await _subject.GetPolicyAsync(ctx, "wrong_name"); _mockPolicy.WasCalled.Should().BeFalse(); _mockInner.WasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task origin_same_as_server_should_not_call_policy() { _allowedPaths.AddRange(new string[] { "/foo" }); Init(); var ctx = new DefaultHttpContext(); ctx.Request.Scheme = "https"; ctx.Request.Host = new HostString("server"); ctx.Request.Path = new PathString("/foo"); ctx.Request.Headers.Append("Origin", "https://server"); var response = await _subject.GetPolicyAsync(ctx, _options.Cors.CorsPolicyName); _mockPolicy.WasCalled.Should().BeFalse(); _mockInner.WasCalled.Should().BeFalse(); } [Theory] [InlineData("https://notserver")] [InlineData("http://server")] [Trait("Category", Category)] public async Task origin_not_same_as_server_should_call_policy(string origin) { _allowedPaths.AddRange(new string[] { "/foo" }); Init(); var ctx = new DefaultHttpContext(); ctx.Request.Scheme = "https"; ctx.Request.Host = new HostString("server"); ctx.Request.Path = new PathString("/foo"); ctx.Request.Headers.Append("Origin", origin); var response = await _subject.GetPolicyAsync(ctx, _options.Cors.CorsPolicyName); _mockPolicy.WasCalled.Should().BeTrue(); _mockInner.WasCalled.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Authorize/AuthorizeCallbackEndpointTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Endpoints; using IdentityServer8.Endpoints.Results; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Endpoints.Authorize { public class AuthorizeCallbackEndpointTests { private const string Category = "Authorize Endpoint"; private HttpContext _context; private TestEventService _fakeEventService = new TestEventService(); private ILogger _fakeLogger = TestLogger.Create(); private IdentityServerOptions _options = new IdentityServerOptions(); private MockConsentMessageStore _mockUserConsentResponseMessageStore = new MockConsentMessageStore(); private MockUserSession _mockUserSession = new MockUserSession(); private NameValueCollection _params = new NameValueCollection(); private StubAuthorizeRequestValidator _stubAuthorizeRequestValidator = new StubAuthorizeRequestValidator(); private StubAuthorizeResponseGenerator _stubAuthorizeResponseGenerator = new StubAuthorizeResponseGenerator(); private StubAuthorizeInteractionResponseGenerator _stubInteractionGenerator = new StubAuthorizeInteractionResponseGenerator(); private AuthorizeCallbackEndpoint _subject; private ClaimsPrincipal _user = new IdentityServerUser("bob").CreatePrincipal(); private ValidatedAuthorizeRequest _validatedAuthorizeRequest; public AuthorizeCallbackEndpointTests() { Init(); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_authorize_after_consent_path_should_return_authorization_result() { var parameters = new NameValueCollection() { { "client_id", "client" }, { "nonce", "some_nonce" }, { "scope", "api1 api2" } }; var request = new ConsentRequest(parameters, _user.GetSubjectId()); _mockUserConsentResponseMessageStore.Messages.Add(request.Id, new Message(new ConsentResponse())); _mockUserSession.User = _user; _context.Request.Method = "GET"; _context.Request.Path = new PathString("/connect/authorize/callback"); _context.Request.QueryString = new QueryString("?" + parameters.ToQueryString()); var result = await _subject.ProcessAsync(_context); result.Should().BeOfType(); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_authorize_after_login_path_should_return_authorization_result() { _context.Request.Method = "GET"; _context.Request.Path = new PathString("/connect/authorize/callback"); _mockUserSession.User = _user; var result = await _subject.ProcessAsync(_context); result.Should().BeOfType(); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_consent_missing_consent_data_should_return_error_page() { var parameters = new NameValueCollection() { { "client_id", "client" }, { "nonce", "some_nonce" }, { "scope", "api1 api2" } }; var request = new ConsentRequest(parameters, _user.GetSubjectId()); _mockUserConsentResponseMessageStore.Messages.Add(request.Id, new Message(null)); _mockUserSession.User = _user; _context.Request.Method = "GET"; _context.Request.Path = new PathString("/connect/authorize/callback"); _context.Request.QueryString = new QueryString("?" + parameters.ToQueryString()); var result = await _subject.ProcessAsync(_context); result.Should().BeOfType(); ((AuthorizeResult)result).Response.IsError.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_no_consent_message_should_return_redirect_for_consent() { _stubInteractionGenerator.Response.IsConsent = true; var parameters = new NameValueCollection() { { "client_id", "client" }, { "nonce", "some_nonce" }, { "scope", "api1 api2" } }; var request = new ConsentRequest(parameters, _user.GetSubjectId()); _mockUserConsentResponseMessageStore.Messages.Add(request.Id, null); _mockUserSession.User = _user; _context.Request.Method = "GET"; _context.Request.Path = new PathString("/connect/authorize/callback"); _context.Request.QueryString = new QueryString("?" + parameters.ToQueryString()); var result = await _subject.ProcessAsync(_context); result.Should().BeOfType(); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_post_to_entry_point_should_return_405() { _context.Request.Method = "POST"; var result = await _subject.ProcessAsync(_context); var statusCode = result as StatusCodeResult; statusCode.Should().NotBeNull(); statusCode.StatusCode.Should().Be(405); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_valid_consent_message_should_cleanup_consent_cookie() { var parameters = new NameValueCollection() { { "client_id", "client" }, { "nonce", "some_nonce" }, { "scope", "api1 api2" } }; var request = new ConsentRequest(parameters, _user.GetSubjectId()); _mockUserConsentResponseMessageStore.Messages.Add(request.Id, new Message(new ConsentResponse() { ScopesValuesConsented = new string[] { "api1", "api2" } })); _mockUserSession.User = _user; _context.Request.Method = "GET"; _context.Request.Path = new PathString("/connect/authorize/callback"); _context.Request.QueryString = new QueryString("?" + parameters.ToQueryString()); var result = await _subject.ProcessAsync(_context); _mockUserConsentResponseMessageStore.Messages.Count.Should().Be(0); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_valid_consent_message_should_return_authorize_result() { var parameters = new NameValueCollection() { { "client_id", "client" }, { "nonce", "some_nonce" }, { "scope", "api1 api2" } }; var request = new ConsentRequest(parameters, _user.GetSubjectId()); _mockUserConsentResponseMessageStore.Messages.Add(request.Id, new Message(new ConsentResponse() { ScopesValuesConsented = new string[] { "api1", "api2" } })); _mockUserSession.User = _user; _context.Request.Method = "GET"; _context.Request.Path = new PathString("/connect/authorize/callback"); _context.Request.QueryString = new QueryString("?" + parameters.ToQueryString()); var result = await _subject.ProcessAsync(_context); result.Should().BeOfType(); } internal void Init() { _context = new MockHttpContextAccessor().HttpContext; _validatedAuthorizeRequest = new ValidatedAuthorizeRequest() { RedirectUri = "http://client/callback", State = "123", ResponseMode = "fragment", ClientId = "client", Client = new Client { ClientId = "client", ClientName = "Test Client" }, Raw = _params, Subject = _user }; _stubAuthorizeResponseGenerator.Response.Request = _validatedAuthorizeRequest; _stubAuthorizeRequestValidator.Result = new AuthorizeRequestValidationResult(_validatedAuthorizeRequest); _subject = new AuthorizeCallbackEndpoint( _fakeEventService, _fakeLogger, _options, _stubAuthorizeRequestValidator, _stubInteractionGenerator, _stubAuthorizeResponseGenerator, _mockUserSession, _mockUserConsentResponseMessageStore); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Authorize/AuthorizeEndpointBaseTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Endpoints; using IdentityServer8.Endpoints.Results; using IdentityServer8.Hosting; using IdentityServer8.Models; using IdentityServer8.ResponseHandling; using IdentityServer8.Services; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Endpoints.Authorize { public class AuthorizeEndpointBaseTests { private const string Category = "Authorize Endpoint"; private HttpContext _context; private TestEventService _fakeEventService = new TestEventService(); private ILogger _fakeLogger = TestLogger.Create(); private IdentityServerOptions _options = new IdentityServerOptions(); private MockUserSession _mockUserSession = new MockUserSession(); private NameValueCollection _params = new NameValueCollection(); private StubAuthorizeRequestValidator _stubAuthorizeRequestValidator = new StubAuthorizeRequestValidator(); private StubAuthorizeResponseGenerator _stubAuthorizeResponseGenerator = new StubAuthorizeResponseGenerator(); private StubAuthorizeInteractionResponseGenerator _stubInteractionGenerator = new StubAuthorizeInteractionResponseGenerator(); private TestAuthorizeEndpoint _subject; private ClaimsPrincipal _user = new IdentityServerUser("bob").CreatePrincipal(); private ValidatedAuthorizeRequest _validatedAuthorizeRequest; public AuthorizeEndpointBaseTests() { Init(); } [Fact] [Trait("Category", Category)] public async Task error_resurect_with_prompt_none_should_include_session_state_in_response() { _params.Add("prompt", "none"); _stubAuthorizeRequestValidator.Result.ValidatedRequest.IsOpenIdRequest = true; _stubAuthorizeRequestValidator.Result.ValidatedRequest.ClientId = "client"; _stubAuthorizeRequestValidator.Result.ValidatedRequest.SessionId = "some_session"; _stubAuthorizeRequestValidator.Result.ValidatedRequest.RedirectUri = "http://redirect"; _stubAuthorizeRequestValidator.Result.IsError = true; _stubAuthorizeRequestValidator.Result.Error = "login_required"; var result = await _subject.ProcessAuthorizeRequestAsync(_params, _user, null); result.Should().BeOfType(); ((AuthorizeResult)result).Response.IsError.Should().BeTrue(); ((AuthorizeResult)result).Response.SessionState.Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task authorize_request_validation_produces_error_should_display_error_page() { _stubAuthorizeRequestValidator.Result.IsError = true; _stubAuthorizeRequestValidator.Result.Error = "some_error"; var result = await _subject.ProcessAuthorizeRequestAsync(_params, _user, null); result.Should().BeOfType(); ((AuthorizeResult)result).Response.IsError.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task interaction_generator_consent_produces_consent_should_show_consent_page() { _stubInteractionGenerator.Response.IsConsent = true; var result = await _subject.ProcessAuthorizeRequestAsync(_params, _user, null); result.Should().BeOfType(); } [Fact] [Trait("Category", Category)] public async Task interaction_produces_error_should_show_error_page() { _stubInteractionGenerator.Response.Error = "error"; var result = await _subject.ProcessAuthorizeRequestAsync(_params, _user, null); result.Should().BeOfType(); ((AuthorizeResult)result).Response.IsError.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task interaction_produces_error_should_show_error_page_with_error_description_if_present() { var errorDescription = "some error description"; _stubInteractionGenerator.Response.Error = "error"; _stubInteractionGenerator.Response.ErrorDescription = errorDescription; var result = await _subject.ProcessAuthorizeRequestAsync(_params, _user, null); result.Should().BeOfType(); var authorizeResult = ((AuthorizeResult)result); authorizeResult.Response.IsError.Should().BeTrue(); authorizeResult.Response.ErrorDescription.Should().Be(errorDescription); } [Fact] [Trait("Category", Category)] public async Task interaction_produces_login_result_should_trigger_login() { _stubInteractionGenerator.Response.IsLogin = true; var result = await _subject.ProcessAuthorizeRequestAsync(_params, _user, null); result.Should().BeOfType(); } [Fact] [Trait("Category", Category)] public async Task ProcessAuthorizeRequestAsync_custom_interaction_redirect_result_should_issue_redirect() { _mockUserSession.User = _user; _stubInteractionGenerator.Response.RedirectUrl = "http://foo.com"; var result = await _subject.ProcessAuthorizeRequestAsync(_params, _user, null); result.Should().BeOfType(); } [Fact] [Trait("Category", Category)] public async Task successful_authorization_request_should_generate_authorize_result() { var result = await _subject.ProcessAuthorizeRequestAsync(_params, _user, null); result.Should().BeOfType(); } internal void Init() { _context = new MockHttpContextAccessor().HttpContext; _validatedAuthorizeRequest = new ValidatedAuthorizeRequest() { RedirectUri = "http://client/callback", State = "123", ResponseMode = "fragment", ClientId = "client", Client = new Client { ClientId = "client", ClientName = "Test Client" }, Raw = _params, Subject = _user }; _stubAuthorizeResponseGenerator.Response.Request = _validatedAuthorizeRequest; _stubAuthorizeRequestValidator.Result = new AuthorizeRequestValidationResult(_validatedAuthorizeRequest); _subject = new TestAuthorizeEndpoint( _fakeEventService, _fakeLogger, _options, _stubAuthorizeRequestValidator, _stubInteractionGenerator, _stubAuthorizeResponseGenerator, _mockUserSession); } internal class TestAuthorizeEndpoint : AuthorizeEndpointBase { public TestAuthorizeEndpoint( IEventService events, ILogger logger, IdentityServerOptions options, IAuthorizeRequestValidator validator, IAuthorizeInteractionResponseGenerator interactionGenerator, IAuthorizeResponseGenerator authorizeResponseGenerator, IUserSession userSession) : base(events, logger, options, validator, interactionGenerator, authorizeResponseGenerator, userSession) { } public override Task ProcessAsync(HttpContext context) { throw new System.NotImplementedException(); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Authorize/AuthorizeEndpointTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Endpoints; using IdentityServer8.Endpoints.Results; using IdentityServer8.Models; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Endpoints.Authorize { public class AuthorizeEndpointTests { private const string Category = "Authorize Endpoint"; private HttpContext _context; private TestEventService _fakeEventService = new TestEventService(); private ILogger _fakeLogger = TestLogger.Create(); private IdentityServerOptions _options = new IdentityServerOptions(); private MockUserSession _mockUserSession = new MockUserSession(); private NameValueCollection _params = new NameValueCollection(); private StubAuthorizeRequestValidator _stubAuthorizeRequestValidator = new StubAuthorizeRequestValidator(); private StubAuthorizeResponseGenerator _stubAuthorizeResponseGenerator = new StubAuthorizeResponseGenerator(); private StubAuthorizeInteractionResponseGenerator _stubInteractionGenerator = new StubAuthorizeInteractionResponseGenerator(); private AuthorizeEndpoint _subject; private ClaimsPrincipal _user = new IdentityServerUser("bob").CreatePrincipal(); private ValidatedAuthorizeRequest _validatedAuthorizeRequest; public AuthorizeEndpointTests() { Init(); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_authorize_path_should_return_authorization_result() { _context.Request.Method = "GET"; _context.Request.Path = new PathString("/connect/authorize"); _mockUserSession.User = _user; var result = await _subject.ProcessAsync(_context); result.Should().BeOfType(); } [Fact] [Trait("Category", Category)] public async Task ProcessAsync_post_without_form_content_type_should_return_415() { _context.Request.Method = "POST"; var result = await _subject.ProcessAsync(_context); var statusCode = result as StatusCodeResult; statusCode.Should().NotBeNull(); statusCode.StatusCode.Should().Be(415); } internal void Init() { _context = new MockHttpContextAccessor().HttpContext; _validatedAuthorizeRequest = new ValidatedAuthorizeRequest() { RedirectUri = "http://client/callback", State = "123", ResponseMode = "fragment", ClientId = "client", Client = new Client { ClientId = "client", ClientName = "Test Client" }, Raw = _params, Subject = _user }; _stubAuthorizeResponseGenerator.Response.Request = _validatedAuthorizeRequest; _stubAuthorizeRequestValidator.Result = new AuthorizeRequestValidationResult(_validatedAuthorizeRequest); _subject = new AuthorizeEndpoint( _fakeEventService, _fakeLogger, _options, _stubAuthorizeRequestValidator, _stubInteractionGenerator, _stubAuthorizeResponseGenerator, _mockUserSession); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Authorize/StubAuthorizeInteractionResponseGenerator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.ResponseHandling; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Endpoints.Authorize { internal class StubAuthorizeInteractionResponseGenerator : IAuthorizeInteractionResponseGenerator { internal InteractionResponse Response { get; set; } = new InteractionResponse(); public Task ProcessInteractionAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null) { return Task.FromResult(Response); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Authorize/StubAuthorizeRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Endpoints.Authorize { public class StubAuthorizeRequestValidator : IAuthorizeRequestValidator { public AuthorizeRequestValidationResult Result { get; set; } public Task ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject = null) { return Task.FromResult(Result); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/EndSession/EndSessionCallbackEndpointTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Endpoints; using Microsoft.Extensions.Logging; namespace IdentityServer.UnitTests.Endpoints.EndSession { public class EndSessionCallbackEndpointTests { private const string Category = "End Session Callback Endpoint"; StubEndSessionRequestValidator _stubEndSessionRequestValidator = new StubEndSessionRequestValidator(); EndSessionCallbackEndpoint _subject; public EndSessionCallbackEndpointTests() { _subject = new EndSessionCallbackEndpoint( _stubEndSessionRequestValidator, new LoggerFactory().CreateLogger()); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/EndSession/EndSessionCallbackResultTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer8.Configuration; using IdentityServer8.Endpoints.Results; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Xunit; namespace IdentityServer.UnitTests.Endpoints.EndSession { public class EndSessionCallbackResultTests { private const string Category = "End Session Callback Result"; private readonly EndSessionCallbackValidationResult _validationResult; private readonly IdentityServerOptions _options; private readonly EndSessionCallbackResult _subject; public EndSessionCallbackResultTests() { _validationResult = new EndSessionCallbackValidationResult() { IsError = false, }; _options = new IdentityServerOptions(); _subject = new EndSessionCallbackResult(_validationResult, _options); } [Fact] public async Task default_options_should_emit_frame_src_csp_headers() { _validationResult.FrontChannelLogoutUrls = new[] { "http://foo" }; var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; await _subject.ExecuteAsync(ctx); ctx.Response.Headers["Content-Security-Policy"].First().Should().Contain("frame-src http://foo"); } [Fact] public async Task relax_csp_options_should_prevent_frame_src_csp_headers() { _options.Authentication.RequireCspFrameSrcForSignout = false; _validationResult.FrontChannelLogoutUrls = new[] { "http://foo" }; var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; await _subject.ExecuteAsync(ctx); ctx.Response.Headers["Content-Security-Policy"].FirstOrDefault().Should().BeNull(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/EndSession/StubBackChannelLogoutClient.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Endpoints.EndSession { internal class StubBackChannelLogoutClient : IBackChannelLogoutService { public bool SendLogoutsWasCalled { get; set; } public Task SendLogoutNotificationsAsync(LogoutNotificationContext context) { SendLogoutsWasCalled = true; return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/EndSession/StubEndSessionRequestValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Endpoints.EndSession { class StubEndSessionRequestValidator : IEndSessionRequestValidator { public EndSessionValidationResult EndSessionValidationResult { get; set; } = new EndSessionValidationResult(); public EndSessionCallbackValidationResult EndSessionCallbackValidationResult { get; set; } = new EndSessionCallbackValidationResult(); public Task ValidateAsync(NameValueCollection parameters, ClaimsPrincipal subject) { return Task.FromResult(EndSessionValidationResult); } public Task ValidateCallbackAsync(NameValueCollection parameters) { return Task.FromResult(EndSessionCallbackValidationResult); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Results/AuthorizeResultTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.IO; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Endpoints.Results; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.ResponseHandling; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.WebUtilities; using Xunit; namespace IdentityServer.UnitTests.Endpoints.Results { public class AuthorizeResultTests { private AuthorizeResult _subject; private AuthorizeResponse _response = new AuthorizeResponse(); private IdentityServerOptions _options = new IdentityServerOptions(); private MockUserSession _mockUserSession = new MockUserSession(); private MockMessageStore _mockErrorMessageStore = new MockMessageStore(); private DefaultHttpContext _context = new DefaultHttpContext(); public AuthorizeResultTests() { _context.SetIdentityServerOrigin("https://server"); _context.SetIdentityServerBasePath("/"); _context.Response.Body = new MemoryStream(); _options.UserInteraction.ErrorUrl = "~/error"; _options.UserInteraction.ErrorIdParameter = "errorId"; _subject = new AuthorizeResult(_response, _options, _mockUserSession, _mockErrorMessageStore, new StubClock()); } [Fact] public async Task error_should_redirect_to_error_page_and_passs_info() { _response.Error = "some_error"; await _subject.ExecuteAsync(_context); _mockErrorMessageStore.Messages.Count.Should().Be(1); _context.Response.StatusCode.Should().Be(302); var location = _context.Response.Headers["Location"].First(); location.Should().StartWith("https://server/error"); var query = QueryHelpers.ParseQuery(new Uri(location).Query); query["errorId"].First().Should().Be(_mockErrorMessageStore.Messages.First().Key); } [Theory] [InlineData(OidcConstants.AuthorizeErrors.AccountSelectionRequired)] [InlineData(OidcConstants.AuthorizeErrors.LoginRequired)] [InlineData(OidcConstants.AuthorizeErrors.ConsentRequired)] [InlineData(OidcConstants.AuthorizeErrors.InteractionRequired)] public async Task prompt_none_errors_should_return_to_client(string error) { _response.Error = error; _response.Request = new ValidatedAuthorizeRequest { ResponseMode = OidcConstants.ResponseModes.Query, RedirectUri = "http://client/callback", PromptModes = new[] { "none" } }; await _subject.ExecuteAsync(_context); _mockUserSession.Clients.Count.Should().Be(0); _context.Response.StatusCode.Should().Be(302); var location = _context.Response.Headers["Location"].First(); location.Should().StartWith("http://client/callback"); } [Theory] [InlineData(OidcConstants.AuthorizeErrors.AccountSelectionRequired)] [InlineData(OidcConstants.AuthorizeErrors.LoginRequired)] [InlineData(OidcConstants.AuthorizeErrors.ConsentRequired)] [InlineData(OidcConstants.AuthorizeErrors.InteractionRequired)] public async Task prompt_none_errors_for_anonymous_users_should_include_session_state(string error) { _response.Error = error; _response.Request = new ValidatedAuthorizeRequest { ResponseMode = OidcConstants.ResponseModes.Query, RedirectUri = "http://client/callback", PromptModes = new[] { "none" }, }; _response.SessionState = "some_session_state"; await _subject.ExecuteAsync(_context); _mockUserSession.Clients.Count.Should().Be(0); _context.Response.StatusCode.Should().Be(302); var location = _context.Response.Headers["Location"].First(); location.Should().Contain("session_state=some_session_state"); } [Fact] public async Task access_denied_should_return_to_client() { const string errorDescription = "some error description"; _response.Error = OidcConstants.AuthorizeErrors.AccessDenied; _response.ErrorDescription = errorDescription; _response.Request = new ValidatedAuthorizeRequest { ResponseMode = OidcConstants.ResponseModes.Query, RedirectUri = "http://client/callback" }; await _subject.ExecuteAsync(_context); _mockUserSession.Clients.Count.Should().Be(0); _context.Response.StatusCode.Should().Be(302); var location = _context.Response.Headers["Location"].First(); location.Should().StartWith("http://client/callback"); var queryString = new Uri(location).Query; var queryParams = QueryHelpers.ParseQuery(queryString); queryParams["error"].Should().Equal(OidcConstants.AuthorizeErrors.AccessDenied); queryParams["error_description"].Should().Equal(errorDescription); } [Fact] public async Task success_should_add_client_to_client_list() { _response.Request = new ValidatedAuthorizeRequest { ClientId = "client", ResponseMode = OidcConstants.ResponseModes.Query, RedirectUri = "http://client/callback" }; await _subject.ExecuteAsync(_context); _mockUserSession.Clients.Should().Contain("client"); } [Fact] public async Task query_mode_should_pass_results_in_query() { _response.Request = new ValidatedAuthorizeRequest { ClientId = "client", ResponseMode = OidcConstants.ResponseModes.Query, RedirectUri = "http://client/callback", State = "state" }; await _subject.ExecuteAsync(_context); _context.Response.StatusCode.Should().Be(302); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-store"); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-cache"); _context.Response.Headers["Cache-Control"].First().Should().Contain("max-age=0"); var location = _context.Response.Headers["Location"].First(); location.Should().StartWith("http://client/callback"); location.Should().Contain("?state=state"); } [Fact] public async Task fragment_mode_should_pass_results_in_fragment() { _response.Request = new ValidatedAuthorizeRequest { ClientId = "client", ResponseMode = OidcConstants.ResponseModes.Fragment, RedirectUri = "http://client/callback", State = "state" }; await _subject.ExecuteAsync(_context); _context.Response.StatusCode.Should().Be(302); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-store"); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-cache"); _context.Response.Headers["Cache-Control"].First().Should().Contain("max-age=0"); var location = _context.Response.Headers["Location"].First(); location.Should().StartWith("http://client/callback"); location.Should().Contain("#state=state"); } [Fact] public async Task form_post_mode_should_pass_results_in_body() { _response.Request = new ValidatedAuthorizeRequest { ClientId = "client", ResponseMode = OidcConstants.ResponseModes.FormPost, RedirectUri = "http://client/callback", State = "state" }; await _subject.ExecuteAsync(_context); _context.Response.StatusCode.Should().Be(200); _context.Response.ContentType.Should().StartWith("text/html"); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-store"); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-cache"); _context.Response.Headers["Cache-Control"].First().Should().Contain("max-age=0"); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("default-src 'none';"); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("script-src 'sha256-orD0/VhH8hLqrLxKHD/HUEMdwqX6/0ve7c5hspX5VJ8='"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("default-src 'none';"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("script-src 'sha256-orD0/VhH8hLqrLxKHD/HUEMdwqX6/0ve7c5hspX5VJ8='"); _context.Response.Body.Seek(0, SeekOrigin.Begin); using (var rdr = new StreamReader(_context.Response.Body)) { var html = rdr.ReadToEnd(); html.Should().Contain(""); html.Should().Contain("
    "); html.Should().Contain(""); } } [Fact] public async Task form_post_mode_should_add_unsafe_inline_for_csp_level_1() { _response.Request = new ValidatedAuthorizeRequest { ClientId = "client", ResponseMode = OidcConstants.ResponseModes.FormPost, RedirectUri = "http://client/callback", State = "state" }; _options.Csp.Level = CspLevel.One; await _subject.ExecuteAsync(_context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("script-src 'unsafe-inline' 'sha256-orD0/VhH8hLqrLxKHD/HUEMdwqX6/0ve7c5hspX5VJ8='"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("script-src 'unsafe-inline' 'sha256-orD0/VhH8hLqrLxKHD/HUEMdwqX6/0ve7c5hspX5VJ8='"); } [Fact] public async Task form_post_mode_should_not_add_deprecated_header_when_it_is_disabled() { _response.Request = new ValidatedAuthorizeRequest { ClientId = "client", ResponseMode = OidcConstants.ResponseModes.FormPost, RedirectUri = "http://client/callback", State = "state" }; _options.Csp.AddDeprecatedHeader = false; await _subject.ExecuteAsync(_context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("script-src 'sha256-orD0/VhH8hLqrLxKHD/HUEMdwqX6/0ve7c5hspX5VJ8='"); _context.Response.Headers["X-Content-Security-Policy"].Should().BeEmpty(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Results/CheckSessionResultTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IO; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer8.Configuration; using IdentityServer8.Endpoints.Results; using IdentityServer8.Extensions; using IdentityServer8.Models; using Microsoft.AspNetCore.Http; using Xunit; namespace IdentityServer.UnitTests.Endpoints.Results { public class CheckSessionResultTests { private CheckSessionResult _subject; private IdentityServerOptions _options = new IdentityServerOptions(); private DefaultHttpContext _context = new DefaultHttpContext(); public CheckSessionResultTests() { _context.SetIdentityServerOrigin("https://server"); _context.SetIdentityServerBasePath("/"); _context.Response.Body = new MemoryStream(); _options.Authentication.CheckSessionCookieName = "foobar"; _subject = new CheckSessionResult(_options); } [Fact] public async Task should_pass_results_in_body() { await _subject.ExecuteAsync(_context); _context.Response.StatusCode.Should().Be(200); _context.Response.ContentType.Should().StartWith("text/html"); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("default-src 'none';"); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("script-src 'sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI='"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("default-src 'none';"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("script-src 'sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI='"); _context.Response.Body.Seek(0, SeekOrigin.Begin); using (var rdr = new StreamReader(_context.Response.Body)) { var html = rdr.ReadToEnd(); html.Should().Contain(""); } } [Fact] public async Task form_post_mode_should_add_unsafe_inline_for_csp_level_1() { _options.Csp.Level = CspLevel.One; await _subject.ExecuteAsync(_context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("script-src 'unsafe-inline' 'sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI='"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("script-src 'unsafe-inline' 'sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI='"); } [Fact] public async Task form_post_mode_should_not_add_deprecated_header_when_it_is_disabled() { _options.Csp.AddDeprecatedHeader = false; await _subject.ExecuteAsync(_context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("script-src 'sha256-fa5rxHhZ799izGRP38+h4ud5QXNT0SFaFlh4eqDumBI='"); _context.Response.Headers["X-Content-Security-Policy"].Should().BeEmpty(); } [Theory] [InlineData("foobar")] [InlineData("morefoobar")] public async Task can_change_cached_cookiename(string cookieName) { _options.Authentication.CheckSessionCookieName = cookieName; await _subject.ExecuteAsync(_context); _context.Response.Body.Seek(0, SeekOrigin.Begin); using (var rdr = new StreamReader(_context.Response.Body)) { var html = rdr.ReadToEnd(); html.Should().Contain($""); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionCallbackResultTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IO; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Endpoints.Results; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Xunit; namespace IdentityServer.UnitTests.Endpoints.Results { public class EndSessionCallbackResultTests { private EndSessionCallbackResult _subject; private EndSessionCallbackValidationResult _result = new EndSessionCallbackValidationResult(); private IdentityServerOptions _options = TestIdentityServerOptions.Create(); private DefaultHttpContext _context = new DefaultHttpContext(); public EndSessionCallbackResultTests() { _context.SetIdentityServerOrigin("https://server"); _context.SetIdentityServerBasePath("/"); _context.Response.Body = new MemoryStream(); _subject = new EndSessionCallbackResult(_result, _options); } [Fact] public async Task error_should_return_400() { _result.IsError = true; await _subject.ExecuteAsync(_context); _context.Response.StatusCode.Should().Be(400); } [Fact] public async Task success_should_render_html_and_iframes() { _result.IsError = false; _result.FrontChannelLogoutUrls = new string[] { "http://foo.com", "http://bar.com" }; await _subject.ExecuteAsync(_context); _context.Response.ContentType.Should().StartWith("text/html"); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-store"); _context.Response.Headers["Cache-Control"].First().Should().Contain("no-cache"); _context.Response.Headers["Cache-Control"].First().Should().Contain("max-age=0"); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("default-src 'none';"); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("style-src 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY=';"); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("frame-src http://foo.com http://bar.com"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("default-src 'none';"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("style-src 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY=';"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("frame-src http://foo.com http://bar.com"); _context.Response.Body.Seek(0, SeekOrigin.Begin); using (var rdr = new StreamReader(_context.Response.Body)) { var html = rdr.ReadToEnd(); html.Should().Contain(""); html.Should().Contain(""); } } [Fact] public async Task fsuccess_should_add_unsafe_inline_for_csp_level_1() { _result.IsError = false; _options.Csp.Level = CspLevel.One; await _subject.ExecuteAsync(_context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("style-src 'unsafe-inline' 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY='"); _context.Response.Headers["X-Content-Security-Policy"].First().Should().Contain("style-src 'unsafe-inline' 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY='"); } [Fact] public async Task form_post_mode_should_not_add_deprecated_header_when_it_is_disabled() { _result.IsError = false; _options.Csp.AddDeprecatedHeader = false; await _subject.ExecuteAsync(_context); _context.Response.Headers["Content-Security-Policy"].First().Should().Contain("style-src 'sha256-u+OupXgfekP+x/f6rMdoEAspPCYUtca912isERnoEjY='"); _context.Response.Headers["X-Content-Security-Policy"].Should().BeEmpty(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Endpoints/Results/EndSessionResultTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Endpoints.Results; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.WebUtilities; using Xunit; namespace IdentityServer.UnitTests.Endpoints.Results { public class EndSessionResultTests { private EndSessionResult _subject; private EndSessionValidationResult _result = new EndSessionValidationResult(); private IdentityServerOptions _options = new IdentityServerOptions(); private MockMessageStore _mockLogoutMessageStore = new MockMessageStore(); private DefaultHttpContext _context = new DefaultHttpContext(); public EndSessionResultTests() { _context.SetIdentityServerOrigin("https://server"); _context.SetIdentityServerBasePath("/"); _options.UserInteraction.LogoutUrl = "~/logout"; _options.UserInteraction.LogoutIdParameter = "logoutId"; _subject = new EndSessionResult(_result, _options, new StubClock(), _mockLogoutMessageStore); } [Fact] public async Task validated_signout_should_pass_logout_message() { _result.IsError = false; _result.ValidatedRequest = new ValidatedEndSessionRequest { Client = new Client { ClientId = "client" }, PostLogOutUri = "http://client/post-logout-callback" }; await _subject.ExecuteAsync(_context); _mockLogoutMessageStore.Messages.Count.Should().Be(1); var location = _context.Response.Headers["Location"].Single(); var query = QueryHelpers.ParseQuery(new Uri(location).Query); location.Should().StartWith("https://server/logout"); query["logoutId"].First().Should().Be(_mockLogoutMessageStore.Messages.First().Key); } [Fact] public async Task unvalidated_signout_should_not_pass_logout_message() { _result.IsError = false; await _subject.ExecuteAsync(_context); _mockLogoutMessageStore.Messages.Count.Should().Be(0); var location = _context.Response.Headers["Location"].Single(); var query = QueryHelpers.ParseQuery(new Uri(location).Query); location.Should().StartWith("https://server/logout"); query.Count.Should().Be(0); } [Fact] public async Task error_result_should_not_pass_logout_message() { _result.IsError = true; _result.ValidatedRequest = new ValidatedEndSessionRequest { Client = new Client { ClientId = "client" }, PostLogOutUri = "http://client/post-logout-callback" }; await _subject.ExecuteAsync(_context); _mockLogoutMessageStore.Messages.Count.Should().Be(0); var location = _context.Response.Headers["Location"].Single(); var query = QueryHelpers.ParseQuery(new Uri(location).Query); location.Should().StartWith("https://server/logout"); query.Count.Should().Be(0); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/ApiResourceSigningAlgorithmSelectionTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using FluentAssertions; using IdentityServer8.Models; using Xunit; namespace IdentityServer.UnitTests.Extensions { public class ApiResourceSigningAlgorithmSelectionTests { [Fact] public void Single_resource_no_allowed_algorithms_set_should_return_empty_list() { var resource = new ApiResource(); var allowedAlgorithms = new List { resource }.FindMatchingSigningAlgorithms(); allowedAlgorithms.Count().Should().Be(0); } [Fact] public void Two_resources_no_allowed_algorithms_set_should_return_empty_list() { var resource1 = new ApiResource(); var resource2 = new ApiResource(); var allowedAlgorithms = new List { resource1, resource2 }.FindMatchingSigningAlgorithms(); allowedAlgorithms.Count().Should().Be(0); } [Theory] [InlineData(new [] { "A" }, new [] { "A" }, new [] { "A" })] [InlineData(new [] { "A", "B" }, new [] { "A", "B" }, new [] { "A", "B" })] [InlineData(new [] { "A", "B", "C" }, new [] { "A", "B", "C" }, new [] { "A", "B", "C" })] [InlineData(new [] { "A", "B" }, new [] { "A", "D" }, new [] { "A" })] [InlineData(new [] { "A", "B", "C" }, new [] { "A", "B", "Z" }, new [] { "A", "B" })] [InlineData(new string[] { }, new [] { "B" }, new string[] { "B" })] [InlineData(new string[] { }, new [] { "C", "D" }, new string[] { "C", "D" })] [InlineData(new [] { "A" }, new [] { "B" }, new string[] { })] [InlineData(new [] { "A", "B" }, new [] { "C", "D" }, new string[] { })] public void Two_resources_with_allowed_algorithms_set_should_return_right_values( string[] resource1Algorithms, string[] resource2Algorithms, string[] expectedAlgorithms) { var resource1 = new ApiResource() { AllowedAccessTokenSigningAlgorithms = resource1Algorithms }; var resource2 = new ApiResource { AllowedAccessTokenSigningAlgorithms = resource2Algorithms }; if (expectedAlgorithms.Any()) { var allowedAlgorithms = new List { resource1, resource2 }.FindMatchingSigningAlgorithms(); allowedAlgorithms.Should().BeEquivalentTo(expectedAlgorithms); } else { Action act = () => new List { resource1, resource2 }.FindMatchingSigningAlgorithms(); act.Should().Throw(); } } [Theory] [InlineData(new [] { "A" }, new [] { "A" }, new [] { "A" }, new [] { "A" })] [InlineData(new [] { "A", "B" }, new [] { "A", "B" }, new [] { "A", "B" }, new [] { "A", "B" })] [InlineData(new [] { "A", "B", "C" }, new [] { "A", "B", "C" }, new [] { "A", "B", "C" }, new [] { "A", "B", "C" })] [InlineData(new [] { "A", "B" }, new [] { "A", "D" }, new [] { "A", "E" } , new [] { "A" })] [InlineData(new [] { "A", "B", "X" }, new [] { "A", "B", "Y" }, new [] { "A", "B", "Z" }, new [] { "A", "B" })] [InlineData(new [] { "A", "B", "X" }, new [] { "C", "D", "X" }, new [] { "E", "F", "X" }, new [] { "X" })] [InlineData(new[] { "A", "B" }, new[] { "A", "D" }, new string[] { }, new[] { "A" })] [InlineData(new[] { "A", "B" }, new[] { "A", "C", "B" }, new string[] { }, new[] { "A", "B" })] [InlineData(new[] { "A", "B" }, new string[] { }, new string[] { }, new[] { "A", "B" })] [InlineData(new [] { "A" }, new [] { "B" }, new [] { "C" }, new string[] { })] [InlineData(new [] { "A", "B" }, new [] { "C", "D" }, new [] { "X", "Y" }, new string[] { })] [InlineData(new [] { "A", "B", "C" }, new [] { "C", "D", "E" }, new [] { "E", "F", "G" }, new string[] { })] public void Three_resources_with_allowed_algorithms_set_should_return_right_values( string[] resource1Algorithms, string[] resource2Algorithms, string[] resource3Algorithms, string[] expectedAlgorithms) { var resource1 = new ApiResource() { AllowedAccessTokenSigningAlgorithms = resource1Algorithms }; var resource2 = new ApiResource { AllowedAccessTokenSigningAlgorithms = resource2Algorithms }; var resource3 = new ApiResource { AllowedAccessTokenSigningAlgorithms = resource3Algorithms }; if (expectedAlgorithms.Any()) { var allowedAlgorithms = new List {resource1, resource2, resource3}.FindMatchingSigningAlgorithms(); allowedAlgorithms.Should().BeEquivalentTo(expectedAlgorithms); } else { Action act = () => new List {resource1, resource2, resource3}.FindMatchingSigningAlgorithms(); act.Should().Throw(); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/EndpointOptionsExtensionsTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Configuration; using IdentityServer8.Extensions; using IdentityServer8.Hosting; using Xunit; using static IdentityServer8.Constants; namespace IdentityServer.UnitTests.Extensions { public class EndpointOptionsExtensionsTests { private readonly EndpointsOptions _options = new EndpointsOptions(); [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForAuthorizeEndpoint(bool expectedIsEndpointEnabled) { _options.EnableAuthorizeEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.Authorize))); } [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForCheckSessionEndpoint(bool expectedIsEndpointEnabled) { _options.EnableCheckSessionEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.CheckSession))); } [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForDeviceAuthorizationEndpoint(bool expectedIsEndpointEnabled) { _options.EnableDeviceAuthorizationEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.DeviceAuthorization))); } [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForDiscoveryEndpoint(bool expectedIsEndpointEnabled) { _options.EnableDiscoveryEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.Discovery))); } [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForEndSessionEndpoint(bool expectedIsEndpointEnabled) { _options.EnableEndSessionEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.EndSession))); } [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForIntrospectionEndpoint(bool expectedIsEndpointEnabled) { _options.EnableIntrospectionEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.Introspection))); } [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForTokenEndpoint(bool expectedIsEndpointEnabled) { _options.EnableTokenEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.Token))); } [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForRevocationEndpoint(bool expectedIsEndpointEnabled) { _options.EnableTokenRevocationEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.Revocation))); } [Theory] [InlineData(true)] [InlineData(false)] public void IsEndpointEnabledShouldReturnExpectedForUserInfoEndpoint(bool expectedIsEndpointEnabled) { _options.EnableUserInfoEndpoint = expectedIsEndpointEnabled; Assert.Equal( expectedIsEndpointEnabled, _options.IsEndpointEnabled( CreateTestEndpoint(EndpointNames.UserInfo))); } private Endpoint CreateTestEndpoint(string name) { return new Endpoint(name, "", null); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/HttpRequestExtensionsTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer8.Extensions; using Microsoft.AspNetCore.Http; using Xunit; namespace IdentityServer.UnitTests.Extensions { public class HttpRequestExtensionsTests { [Fact] public void GetCorsOrigin_valid_cors_request_should_return_cors_origin() { var ctx = new DefaultHttpContext(); ctx.Request.Scheme = "http"; ctx.Request.Host = new HostString("foo"); ctx.Request.Headers.Append("Origin", "http://bar"); ctx.Request.GetCorsOrigin().Should().Be("http://bar"); } [Fact] public void GetCorsOrigin_origin_from_same_host_should_not_return_cors_origin() { var ctx = new DefaultHttpContext(); ctx.Request.Scheme = "http"; ctx.Request.Host = new HostString("foo"); ctx.Request.Headers.Append("Origin", "http://foo"); ctx.Request.GetCorsOrigin().Should().BeNull(); } [Fact] public void GetCorsOrigin_no_origin_should_not_return_cors_origin() { var ctx = new DefaultHttpContext(); ctx.Request.Scheme = "http"; ctx.Request.Host = new HostString("foo"); ctx.Request.GetCorsOrigin().Should().BeNull(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/IResourceStoreExtensionsTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Extensions { public class IResourceStoreExtensionsTests { [Fact] public async Task GetAllEnabledResourcesAsync_on_duplicate_identity_scopes_should_fail() { var store = new MockResourceStore() { IdentityResources = { new IdentityResource { Name = "A" }, new IdentityResource { Name = "A" } } }; Func a = async () => await store.GetAllEnabledResourcesAsync(); (await a.Should().ThrowAsync()).And.Message.ToLowerInvariant().Should().Contain("duplicate").And.Contain("identity scopes"); } [Fact] public async Task GetAllEnabledResourcesAsync_without_duplicate_identity_scopes_should_succeed() { var store = new MockResourceStore() { IdentityResources = { new IdentityResource { Name = "A" }, new IdentityResource { Name = "B" } } }; await store.GetAllEnabledResourcesAsync(); } [Fact] public async Task GetAllEnabledResourcesAsync_on_duplicate_api_resources_should_fail() { var store = new MockResourceStore() { ApiResources = { new ApiResource { Name = "a" }, new ApiResource { Name = "a" } } }; Func a = async () => await store.GetAllEnabledResourcesAsync(); (await a.Should().ThrowAsync()) .And.Message.ToLowerInvariant().Should().Contain("duplicate").And.Contain("api resources"); } [Fact] public async Task GetAllEnabledResourcesAsync_without_duplicate_api_scopes_should_succeed() { var store = new MockResourceStore() { ApiResources = { new ApiResource("A"), new ApiResource("B") } }; await store.GetAllEnabledResourcesAsync(); } [Fact] public async Task FindResourcesByScopeAsync_on_duplicate_identity_scopes_should_fail() { var store = new MockResourceStore() { IdentityResources = { new IdentityResource { Name = "A" }, new IdentityResource { Name = "A" } } }; Func a = async () => await store.FindResourcesByScopeAsync(new string[] { "A" }); (await a.Should().ThrowAsync()).And.Message.ToLowerInvariant().Should().Contain("duplicate").And.Contain("identity scopes"); } [Fact] public async Task FindResourcesByScopeAsync_without_duplicate_identity_scopes_should_succeed() { var store = new MockResourceStore() { IdentityResources = { new IdentityResource { Name = "A" }, new IdentityResource { Name = "B" } } }; await store.FindResourcesByScopeAsync(new string[] { "A" }); } [Fact] public async Task FindResourcesByScopeAsync_on_duplicate_api_scopes_should_succeed() { var store = new MockResourceStore() { ApiResources = { new ApiResource { Name = "api1", Scopes = { "a" } }, new ApiResource() { Name = "api2", Scopes = { "a" } }, }, ApiScopes = { new ApiScope("a") } }; var result = await store.FindResourcesByScopeAsync(new string[] { "a" }); result.ApiResources.Count.Should().Be(2); result.ApiScopes.Count.Should().Be(1); result.ApiResources.Select(x => x.Name).Should().BeEquivalentTo(new[] { "api1", "api2" }); result.ApiScopes.Select(x => x.Name).Should().BeEquivalentTo(new[] { "a" }); } [Fact] public async Task FindResourcesByScopeAsync_without_duplicate_api_scopes_should_succeed() { var store = new MockResourceStore() { ApiResources = { new ApiResource("A"), new ApiResource("B") } }; await store.FindResourcesByScopeAsync(new string[] { "A" }); } [Fact] public async Task FindResourcesByScopeAsync_with_duplicate_api_scopes_on_single_api_resource_should_succeed_and_only_reuturn_one_resource() { var store = new MockResourceStore() { ApiResources = { new ApiResource { Name = "api1", Scopes = { "a", "a" } } }, ApiScopes = { new ApiScope("a"), } }; var result = await store.FindResourcesByScopeAsync(new string[] { "a" }); result.ApiResources.Count.Should().Be(1); } public class MockResourceStore : IResourceStore { public List IdentityResources { get; set; } = new List(); public List ApiResources { get; set; } = new List(); public List ApiScopes { get; set; } = new List(); public Task> FindApiResourcesByNameAsync(IEnumerable names) { var apis = from a in ApiResources where names.Contains(a.Name) select a; return Task.FromResult(apis); } public Task> FindApiResourcesByScopeNameAsync(IEnumerable names) { if (names == null) throw new ArgumentNullException(nameof(names)); var api = from a in ApiResources where a.Scopes.Any(x => names.Contains(x)) select a; return Task.FromResult(api); } public Task> FindIdentityResourcesByScopeNameAsync(IEnumerable names) { if (names == null) throw new ArgumentNullException(nameof(names)); var identity = from i in IdentityResources where names.Contains(i.Name) select i; return Task.FromResult(identity); } public Task> FindApiScopesByNameAsync(IEnumerable scopeNames) { var q = from x in ApiScopes where scopeNames.Contains(x.Name) select x; return Task.FromResult(q); } public Task GetAllResourcesAsync() { var result = new Resources(IdentityResources, ApiResources, ApiScopes); return Task.FromResult(result); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/IdentityServerBuilderExtensionsCacheStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Stores; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer.UnitTests.Extensions { public class IdentityServerBuilderExtensionsCacheStoreTests { private class CustomClientStore: IClientStore { public Task FindClientByIdAsync(string clientId) { throw new System.NotImplementedException(); } } private class CustomResourceStore : IResourceStore { public Task> FindIdentityResourcesByScopeNameAsync(IEnumerable scopeNames) { throw new System.NotImplementedException(); } public Task> FindApiResourcesByScopeNameAsync(IEnumerable scopeNames) { throw new System.NotImplementedException(); } public Task> FindApiResourcesByNameAsync(IEnumerable names) { throw new System.NotImplementedException(); } public Task GetAllResourcesAsync() { throw new System.NotImplementedException(); } public Task> FindApiScopesByNameAsync(IEnumerable scopeNames) { throw new System.NotImplementedException(); } } [Fact] public void AddClientStoreCache_should_add_concrete_iclientstore_implementation() { var services = new ServiceCollection(); var identityServerBuilder = new IdentityServerBuilder(services); identityServerBuilder.AddClientStoreCache(); services.Any(x => x.ImplementationType == typeof(CustomClientStore)).Should().BeTrue(); } [Fact] public void AddResourceStoreCache_should_attempt_to_register_iresourcestore_implementation() { var services = new ServiceCollection(); var identityServerBuilder = new IdentityServerBuilder(services); identityServerBuilder.AddResourceStoreCache(); services.Any(x => x.ImplementationType == typeof(CustomResourceStore)).Should().BeTrue(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/IdentityServerBuilderExtensionsCryptoTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8; using IdentityServer8.Configuration; using Microsoft.Extensions.DependencyInjection; using Microsoft.IdentityModel.Tokens; using System; using System.IO; using System.Security.Cryptography; using Xunit; namespace IdentityServer.UnitTests.Extensions { public class IdentityServerBuilderExtensionsCryptoTests { [Fact] public void AddSigningCredential_with_json_web_key_containing_asymmetric_key_should_succeed() { IServiceCollection services = new ServiceCollection(); IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services); String json = @"{ ""alg"" : ""RS256"", ""kty"" : ""RSA"", ""use"" : ""sig"", ""d"" : ""KGGNkbbgm2hNMqW6fP1fmcWwEBy77WOJIPAXnDJ0KxNTtqDF8K5ULj7EElHO1A8ZnNl1Ey/x//G9lJCOQUU9wmj010dOSsW0NBbR5NtRtLLuVbkVdyft53PGeTQs+1S3c51fz9jojtNqmlfXSANPFOH6QhxmzpTx3KLsf/TpCzblkSrEGOOqCCvVdl7ybTcB230jNhh3JoL7po1rvxKtoOM4a/Bs0NtKj7e+VaHcf0GLnBPJYetsHu43ZfNejJeDoouaXZzeVEklY3B0pe10OTCIOu0JUKGZxNekklRIo1WSEYdL+CJfrSKWIv8bLj6xSr5zrASvWODyH443LN6ZvQ=="", ""e"" : ""AQAB"", ""n"" : ""q7mZfquRq8tzg/5slbNdQmrosNN/mFXS25dbSPm11qEDCgZa452KkO8+hvMtqa92QaqdlmalSF8+FRDOz3grDR5NtmnXZxuKnp+raKfzpC6hCvh2JSIe/J9enmsMM4YeI4d1FOSDwhJlZIYMdMnqG/VJtO1LSHjOaF3XN31ANKF0nPAsmr2/WysiQlxnxxiikLEnsFuNdS615ODDXFGTQ1E+zc4zVur4/Ox0cllPwHPA4PqoIgdPJPL+xM9IOIXuAGtsp4CYoxT6VWaRrALIZXXDY806WGTuctq4KKot6FGL9HQte2hRLl4E/r8SzIK86U3wRwrBe7saK+XUXoP0gQ="", ""p"" : ""25dkucyCSqxRcJpRrhl7PXqw7wqBZeLQgYlZLpK493PdM8pFfq+/LK1hFtxIjdFKqXS/TOikB4YCBMEH0Im3HZ8Lo0dub3SWNhdegJyRjMbcoO+A9YSODEj7DFaNpZtdmtDi1n6etJm66ctPSR20NNpzoYZuaJ92fVQiKiOh6Qs="", ""q"" : ""yDKBrS8l1DOx4dwP9hdwhqZJ3XahidiIZSL7m46I/6+cjaki/1mtNiA60MOgqTKegP7Fo7jAYvliqQwnvVGmQvLv19cfKywlIuKN9DdkLHnKh75hfo7aakEbO7GJ5zVgsNnKOdf8wvpclfvIuRDEVva4cksPzsJy6K7C8ENCSCM="", ""dp"" : ""GlYJ6o6wgawxCEQ5z5uWwETau5CS/Fk7kI2ceI14SZVHzlJQC2WglAcnQcqhmQCk57Xsy5iLM6vKyi8sdMJPh+nvR2HlyNA+w7YBy4L7odqn01VmLgv7zVVjZpNq4ZXEoDC1Q+xjtF1LoYaUt7wsRLp+a7znuPyHBXj1sAAeBwk="", ""dq"" : ""W8OK3S83T8VCTBzq1Ap6cb3XLcQq11yBaJpYaj0zXr/IKsbUW+dnFeBAFWEWS3gAX3Bod1tAFB3rs0D3FjhO1XE1ruHUT520iAEAwGiDaj+JLh994NzqELo3GW2PoIM/BtFNeKYgHd9UgQsgPnQJCzOb6Aev/z3yHeW9RRQPVbE="", ""qi"" : ""w4KdmiDN1GtK71JxaasqmEKPNfV3v2KZDXKnfyhUsdx/idKbdTVjvMOkxFPJ4FqV4yIVn06f3QHTm4NEG18Diqxsrzd6kXQIHOa858tLsCcmt9FoGfrgCFgVceh3K/Zah/r8rl9Y61u0Z1kZumwMvFpFE+mVU01t9HgTEAVkHTc="" }"; JsonWebKey jsonWebKey = new JsonWebKey(json); SigningCredentials credentials = new SigningCredentials(jsonWebKey, jsonWebKey.Alg); identityServerBuilder.AddSigningCredential(credentials); } [Fact] public void AddSigningCredential_with_json_web_key_containing_symmetric_key_should_throw_exception() { IServiceCollection services = new ServiceCollection(); IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services); String json = @"{ ""alg"" : ""HS256"", ""kty"" : ""oct"", ""use"" : ""sig"", ""k"" : ""y5FHaQFtC294HLAtPXAcMkxZ5gHzCq24223vSYQUrDuu-3CUw7UzPru-AX30ubeB2IM_gUsNQ80bX22wwSk_3LC6XxYxqeGJZSeoQqHG0VNbaWCVkqeuB_HOiL1-ksPfGT-o8_A_Uv-6zi2NaEOYpnIyff5LpdW__LhiE-bhIenaw7GhoXSAfsGEZfNZpUUOU35NAiN2dv0T5vptb87wkL1I2zLhV0pdLvWsDWgQPINEa8bbCA_mseBYpB1eioZvt0TZbp6CL9tiEoiikYV_F3IutrJ2SOWYtDNFeQ3sbyYP7zTzh9a2eyaM8ca5_q3qosI92AbZ7WpEFLa9cZ_O7g"" }"; JsonWebKey jsonWebKey = new JsonWebKey(json); SigningCredentials credentials = new SigningCredentials(jsonWebKey, jsonWebKey.Alg); Assert.Throws(() => identityServerBuilder.AddSigningCredential(credentials)); } [Fact] public void AddDeveloperSigningCredential_should_succeed() { IServiceCollection services = new ServiceCollection(); IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services); identityServerBuilder.AddDeveloperSigningCredential(); //clean up... delete stored rsa key var filename = Path.Combine(Directory.GetCurrentDirectory(), "tempkey.rsa"); if (File.Exists(filename)) File.Delete(filename); } [Fact] public void AddDeveloperSigningCredential_should_succeed_when_called_multiple_times() { IServiceCollection services = new ServiceCollection(); IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services); try { identityServerBuilder.AddDeveloperSigningCredential(); //calling a second time will try to load the saved rsa key from disk. An exception will be throw if the private key is not serialized properly. identityServerBuilder.AddDeveloperSigningCredential(); } finally { //clean up... delete stored rsa key var filename = Path.Combine(Directory.GetCurrentDirectory(), "tempkey.rsa"); if (File.Exists(filename)) File.Delete(filename); } } [Theory] [InlineData(Constants.CurveOids.P256, SecurityAlgorithms.EcdsaSha256)] [InlineData(Constants.CurveOids.P384, SecurityAlgorithms.EcdsaSha384)] [InlineData(Constants.CurveOids.P521, SecurityAlgorithms.EcdsaSha512)] public void AddSigningCredential_with_valid_curve_should_succeed(string curveOid, string alg) { IServiceCollection services = new ServiceCollection(); IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services); var key = new ECDsaSecurityKey(ECDsa.Create( ECCurve.CreateFromOid(Oid.FromOidValue(curveOid, OidGroup.All)))); identityServerBuilder.AddSigningCredential(key, alg); } [Theory] [InlineData(Constants.CurveOids.P256, SecurityAlgorithms.EcdsaSha512)] [InlineData(Constants.CurveOids.P384, SecurityAlgorithms.EcdsaSha512)] [InlineData(Constants.CurveOids.P521, SecurityAlgorithms.EcdsaSha256)] public void AddSigningCredential_with_invalid_curve_should_throw_exception(string curveOid, string alg) { IServiceCollection services = new ServiceCollection(); IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services); var key = new ECDsaSecurityKey(ECDsa.Create( ECCurve.CreateFromOid(Oid.FromOidValue(curveOid, OidGroup.All)))); Assert.Throws(() => identityServerBuilder.AddSigningCredential(key, alg)); } [Theory] [InlineData(Constants.CurveOids.P256, SecurityAlgorithms.EcdsaSha256, JsonWebKeyECTypes.P256)] [InlineData(Constants.CurveOids.P384, SecurityAlgorithms.EcdsaSha384, JsonWebKeyECTypes.P384)] [InlineData(Constants.CurveOids.P521, SecurityAlgorithms.EcdsaSha512, JsonWebKeyECTypes.P521)] public void AddSigningCredential_with_invalid_crv_value_should_throw_exception(string curveOid, string alg, string crv) { IServiceCollection services = new ServiceCollection(); IIdentityServerBuilder identityServerBuilder = new IdentityServerBuilder(services); var key = new ECDsaSecurityKey(ECDsa.Create( ECCurve.CreateFromOid(Oid.FromOidValue(curveOid, OidGroup.All)))); var parameters = key.ECDsa.ExportParameters(true); var jsonWebKeyFromECDsa = new JsonWebKey() { Kty = JsonWebAlgorithmsKeyTypes.EllipticCurve, Use = "sig", Kid = key.KeyId, KeyId = key.KeyId, X = Base64UrlEncoder.Encode(parameters.Q.X), Y = Base64UrlEncoder.Encode(parameters.Q.Y), D = Base64UrlEncoder.Encode(parameters.D), Crv = crv.Replace("-", string.Empty), Alg = SecurityAlgorithms.EcdsaSha256 }; Assert.Throws(() => identityServerBuilder.AddSigningCredential(jsonWebKeyFromECDsa, alg)); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/JwtPayloadCreationTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Extensions; using IdentityServer8.Models; using Microsoft.AspNetCore.Authentication; using Xunit; namespace IdentityServer.UnitTests.Extensions { public class JwtPayloadCreationTests { private Token _token; public JwtPayloadCreationTests() { var claims = new List { new Claim(JwtClaimTypes.Scope, "scope1"), new Claim(JwtClaimTypes.Scope, "scope2"), new Claim(JwtClaimTypes.Scope, "scope3"), }; _token = new Token(OidcConstants.TokenTypes.AccessToken) { CreationTime = DateTime.UtcNow, Issuer = "issuer", Lifetime = 60, Claims = claims.Distinct(new ClaimComparer()).ToList(), ClientId = "client" }; } [Fact] public void Should_create_scopes_as_array_by_default() { var options = new IdentityServerOptions(); var payload = _token.CreateJwtPayload(new SystemClock(), options, TestLogger.Create()); payload.Should().NotBeNull(); var scopes = payload.Claims.Where(c => c.Type == JwtClaimTypes.Scope).ToArray(); scopes.Count().Should().Be(3); scopes[0].Value.Should().Be("scope1"); scopes[1].Value.Should().Be("scope2"); scopes[2].Value.Should().Be("scope3"); } [Fact] public void Should_create_scopes_as_string() { var options = new IdentityServerOptions { EmitScopesAsSpaceDelimitedStringInJwt = true }; var payload = _token.CreateJwtPayload(new SystemClock(), options, TestLogger.Create()); payload.Should().NotBeNull(); var scopes = payload.Claims.Where(c => c.Type == JwtClaimTypes.Scope).ToList(); scopes.Count().Should().Be(1); scopes.First().Value.Should().Be("scope1 scope2 scope3"); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/StringExtensionsTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Extensions; using Xunit; namespace IdentityServer.UnitTests.Extensions { public class StringExtensionsTests { private void CheckOrigin(string inputUrl, string expectedOrigin) { var actualOrigin = inputUrl.GetOrigin(); Assert.Equal(expectedOrigin, actualOrigin); } [Fact] public void TestGetOrigin() { CheckOrigin("http://idsvr.com", "http://idsvr.com"); CheckOrigin("http://idsvr.com/", "http://idsvr.com"); CheckOrigin("http://idsvr.com/test", "http://idsvr.com"); CheckOrigin("http://idsvr.com/test/resource", "http://idsvr.com"); CheckOrigin("http://idsvr.com:8080", "http://idsvr.com:8080"); CheckOrigin("http://idsvr.com:8080/", "http://idsvr.com:8080"); CheckOrigin("http://idsvr.com:8080/test", "http://idsvr.com:8080"); CheckOrigin("http://idsvr.com:8080/test/resource", "http://idsvr.com:8080"); CheckOrigin("http://127.0.0.1", "http://127.0.0.1"); CheckOrigin("http://127.0.0.1/", "http://127.0.0.1"); CheckOrigin("http://127.0.0.1/test", "http://127.0.0.1"); CheckOrigin("http://127.0.0.1/test/resource", "http://127.0.0.1"); CheckOrigin("http://127.0.0.1:8080", "http://127.0.0.1:8080"); CheckOrigin("http://127.0.0.1:8080/", "http://127.0.0.1:8080"); CheckOrigin("http://127.0.0.1:8080/test", "http://127.0.0.1:8080"); CheckOrigin("http://127.0.0.1:8080/test/resource", "http://127.0.0.1:8080"); CheckOrigin("http://localhost", "http://localhost"); CheckOrigin("http://localhost/", "http://localhost"); CheckOrigin("http://localhost/test", "http://localhost"); CheckOrigin("http://localhost/test/resource", "http://localhost"); CheckOrigin("http://localhost:8080", "http://localhost:8080"); CheckOrigin("http://localhost:8080/", "http://localhost:8080"); CheckOrigin("http://localhost:8080/test", "http://localhost:8080"); CheckOrigin("http://localhost:8080/test/resource", "http://localhost:8080"); CheckOrigin("https://idsvr.com", "https://idsvr.com"); CheckOrigin("https://idsvr.com/", "https://idsvr.com"); CheckOrigin("https://idsvr.com/test", "https://idsvr.com"); CheckOrigin("https://idsvr.com/test/resource", "https://idsvr.com"); CheckOrigin("https://idsvr.com:8080", "https://idsvr.com:8080"); CheckOrigin("https://idsvr.com:8080/", "https://idsvr.com:8080"); CheckOrigin("https://idsvr.com:8080/test", "https://idsvr.com:8080"); CheckOrigin("https://idsvr.com:8080/test/resource", "https://idsvr.com:8080"); CheckOrigin("https://127.0.0.1", "https://127.0.0.1"); CheckOrigin("https://127.0.0.1/", "https://127.0.0.1"); CheckOrigin("https://127.0.0.1/test", "https://127.0.0.1"); CheckOrigin("https://127.0.0.1/test/resource", "https://127.0.0.1"); CheckOrigin("https://127.0.0.1:8080", "https://127.0.0.1:8080"); CheckOrigin("https://127.0.0.1:8080/", "https://127.0.0.1:8080"); CheckOrigin("https://127.0.0.1:8080/test", "https://127.0.0.1:8080"); CheckOrigin("https://127.0.0.1:8080/test/resource", "https://127.0.0.1:8080"); CheckOrigin("https://localhost", "https://localhost"); CheckOrigin("https://localhost/", "https://localhost"); CheckOrigin("https://localhost/test", "https://localhost"); CheckOrigin("https://localhost/test/resource", "https://localhost"); CheckOrigin("https://localhost:8080", "https://localhost:8080"); CheckOrigin("https://localhost:8080/", "https://localhost:8080"); CheckOrigin("https://localhost:8080/test", "https://localhost:8080"); CheckOrigin("https://localhost:8080/test/resource", "https://localhost:8080"); CheckOrigin("test://idsvr.com", null); CheckOrigin("test://idsvr.com/", null); CheckOrigin("test://idsvr.com/test", null); CheckOrigin("test://idsvr.com/test/resource", null); CheckOrigin("test://idsvr.com:8080", null); CheckOrigin("test://idsvr.com:8080/", null); CheckOrigin("test://idsvr.com:8080/test", null); CheckOrigin("test://idsvr.com:8080/test/resource", null); CheckOrigin("test://127.0.0.1", null); CheckOrigin("test://127.0.0.1/", null); CheckOrigin("test://127.0.0.1/test", null); CheckOrigin("test://127.0.0.1/test/resource", null); CheckOrigin("test://127.0.0.1:8080", null); CheckOrigin("test://127.0.0.1:8080/", null); CheckOrigin("test://127.0.0.1:8080/test", null); CheckOrigin("test://127.0.0.1:8080/test/resource", null); CheckOrigin("test://localhost", null); CheckOrigin("test://localhost/", null); CheckOrigin("test://localhost/test", null); CheckOrigin("test://localhost/test/resource", null); CheckOrigin("test://localhost:8080", null); CheckOrigin("test://localhost:8080/", null); CheckOrigin("test://localhost:8080/test", null); CheckOrigin("test://localhost:8080/test/resource", null); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Extensions/ValidatedAuthorizeRequestExtensionsTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Extensions { public class ValidatedAuthorizeRequestExtensionsTests { [Fact] public void GetAcrValues_should_return_snapshot_of_values() { var request = new ValidatedAuthorizeRequest() { Raw = new System.Collections.Specialized.NameValueCollection() }; request.AuthenticationContextReferenceClasses.Add("a"); request.AuthenticationContextReferenceClasses.Add("b"); request.AuthenticationContextReferenceClasses.Add("c"); var acrs = request.GetAcrValues(); foreach(var acr in acrs) { request.RemoveAcrValue(acr); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Hosting/EndpointRouterTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Hosting; using Microsoft.AspNetCore.Http; using Xunit; using static IdentityServer8.Constants; namespace IdentityServer.UnitTests.Hosting { public class EndpointRouterTests { private Dictionary _pathMap; private List _endpoints; private IdentityServerOptions _options; private EndpointRouter _subject; public EndpointRouterTests() { _pathMap = new Dictionary(); _endpoints = new List(); _options = new IdentityServerOptions(); _subject = new EndpointRouter(_endpoints, _options, TestLogger.Create()); } [Fact] public void Endpoint_ctor_requires_path_to_start_with_slash() { Action a = () => new IdentityServer8.Hosting.Endpoint("ep1", "ep1", typeof(MyEndpointHandler)); a.Should().Throw(); } [Fact] public void Find_should_return_null_for_incorrect_path() { _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep1", "/ep1", typeof(MyEndpointHandler))); _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep2", "/ep2", typeof(MyOtherEndpointHandler))); var ctx = new DefaultHttpContext(); ctx.Request.Path = new PathString("/wrong"); ctx.RequestServices = new StubServiceProvider(); var result = _subject.Find(ctx); result.Should().BeNull(); } [Fact] public void Find_should_find_path() { _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep1", "/ep1", typeof(MyEndpointHandler))); _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep2", "/ep2", typeof(MyOtherEndpointHandler))); var ctx = new DefaultHttpContext(); ctx.Request.Path = new PathString("/ep1"); ctx.RequestServices = new StubServiceProvider(); var result = _subject.Find(ctx); result.Should().BeOfType(); } [Fact] public void Find_should_not_find_nested_paths() { _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep1", "/ep1", typeof(MyEndpointHandler))); _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep2", "/ep2", typeof(MyOtherEndpointHandler))); var ctx = new DefaultHttpContext(); ctx.Request.Path = new PathString("/ep1/subpath"); ctx.RequestServices = new StubServiceProvider(); var result = _subject.Find(ctx); result.Should().BeNull(); } [Fact] public void Find_should_find_first_registered_mapping() { _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep1", "/ep1", typeof(MyEndpointHandler))); _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep1", "/ep1", typeof(MyOtherEndpointHandler))); var ctx = new DefaultHttpContext(); ctx.Request.Path = new PathString("/ep1"); ctx.RequestServices = new StubServiceProvider(); var result = _subject.Find(ctx); result.Should().BeOfType(); } [Fact] public void Find_should_return_null_for_disabled_endpoint() { _endpoints.Add(new IdentityServer8.Hosting.Endpoint(EndpointNames.Authorize, "/ep1", typeof(MyEndpointHandler))); _endpoints.Add(new IdentityServer8.Hosting.Endpoint("ep2", "/ep2", typeof(MyOtherEndpointHandler))); _options.Endpoints.EnableAuthorizeEndpoint = false; var ctx = new DefaultHttpContext(); ctx.Request.Path = new PathString("/ep1"); ctx.RequestServices = new StubServiceProvider(); var result = _subject.Find(ctx); result.Should().BeNull(); } private class MyEndpointHandler : IEndpointHandler { public Task ProcessAsync(HttpContext context) { throw new NotImplementedException(); } } private class MyOtherEndpointHandler : IEndpointHandler { public Task ProcessAsync(HttpContext context) { throw new NotImplementedException(); } } private class StubServiceProvider : IServiceProvider { public object GetService(Type serviceType) { if (serviceType == typeof(MyEndpointHandler)) return new MyEndpointHandler(); if (serviceType == typeof(MyOtherEndpointHandler)) return new MyOtherEndpointHandler(); throw new InvalidOperationException(); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/IdentityServer.UnitTests.csproj ================================================ ../../../../key.snk true true runtime; build; native; contentfiles; analyzers; buildtransitive all Always Always ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Infrastructure/ObjectSerializerTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using FluentAssertions; using IdentityServer8.Models; using Xunit; namespace IdentityServer.UnitTests.Infrastructure { public class ObjectSerializerTests { public ObjectSerializerTests() { } [Fact] public void Can_be_deserialize_message() { Action a = () => IdentityServer8.ObjectSerializer.FromString>("{\"created\":0, \"data\": {\"error\": \"error\"}}"); a.Should().NotThrow(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/ResponseHandling/AuthorizeInteractionResponseGenerator/AuthorizeInteractionResponseGeneratorTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Validation; using Xunit; using static IdentityModel.OidcConstants; namespace IdentityServer.UnitTests.ResponseHandling.AuthorizeInteractionResponseGenerator { public class AuthorizeInteractionResponseGeneratorTests { private IdentityServerOptions _options = new IdentityServerOptions(); private IdentityServer8.ResponseHandling.AuthorizeInteractionResponseGenerator _subject; private MockConsentService _mockConsentService = new MockConsentService(); private StubClock _clock = new StubClock(); public AuthorizeInteractionResponseGeneratorTests() { _subject = new IdentityServer8.ResponseHandling.AuthorizeInteractionResponseGenerator( _clock, TestLogger.Create(), _mockConsentService, new MockProfileService()); } [Fact] public async Task Authenticated_User_with_restricted_current_Idp_with_prompt_none_must_error() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), Client = new Client { EnableLocalLogin = false, IdentityProviderRestrictions = new List { "some_idp" } }, PromptModes = new[] { PromptModes.None }, }; var result = await _subject.ProcessInteractionAsync(request); result.IsError.Should().BeTrue(); result.IsLogin.Should().BeFalse(); } [Fact] public async Task Authenticated_User_with_maxage_with_prompt_none_must_error() { _clock.UtcNowFunc = () => new DateTime(2020, 02, 03, 9, 0, 0); var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123") { AuthenticationTime = new DateTime(2020, 02, 01, 9, 0, 0), IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), Client = new Client { EnableLocalLogin = true, }, PromptModes = new[] { PromptModes.None }, MaxAge = 3600 }; var result = await _subject.ProcessInteractionAsync(request); result.IsError.Should().BeTrue(); result.IsLogin.Should().BeFalse(); } [Fact] public async Task Authenticated_User_with_different_requested_Idp_with_prompt_none_must_error() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client(), AuthenticationContextReferenceClasses = new List{ "idp:some_idp" }, Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), PromptModes = new[] { PromptModes.None } }; var result = await _subject.ProcessInteractionAsync(request); result.IsError.Should().BeTrue(); result.IsLogin.Should().BeFalse(); } [Fact] public async Task Authenticated_User_beyond_client_user_sso_lifetime_with_prompt_none_should_error() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client() { UserSsoLifetime = 3600 // 1h }, Subject = new IdentityServerUser("123") { IdentityProvider = "local", AuthenticationTime = _clock.UtcNow.UtcDateTime.Subtract(TimeSpan.FromSeconds(3700)) }.CreatePrincipal(), PromptModes = new[] { PromptModes.None } }; var result = await _subject.ProcessInteractionAsync(request); result.IsError.Should().BeTrue(); result.IsLogin.Should().BeFalse(); } [Fact] public async Task locally_authenticated_user_but_client_does_not_allow_local_with_prompt_none_should_error() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client() { EnableLocalLogin = false }, Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), PromptModes = new[] { PromptModes.None } }; var result = await _subject.ProcessInteractionAsync(request); result.IsError.Should().BeTrue(); result.IsLogin.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/ResponseHandling/AuthorizeInteractionResponseGenerator/AuthorizeInteractionResponseGeneratorTests_Consent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.ResponseHandling.AuthorizeInteractionResponseGenerator { public class AuthorizeInteractionResponseGeneratorTests_Consent { private IdentityServer8.ResponseHandling.AuthorizeInteractionResponseGenerator _subject; private IdentityServerOptions _options = new IdentityServerOptions(); private MockConsentService _mockConsent = new MockConsentService(); private MockProfileService _fakeUserService = new MockProfileService(); private void RequiresConsent(bool value) { _mockConsent.RequiresConsentResult = value; } private void AssertUpdateConsentNotCalled() { _mockConsent.ConsentClient.Should().BeNull(); _mockConsent.ConsentSubject.Should().BeNull(); _mockConsent.ConsentScopes.Should().BeNull(); } private void AssertUpdateConsentCalled(Client client, ClaimsPrincipal user, params string[] scopes) { _mockConsent.ConsentClient.Should().BeSameAs(client); _mockConsent.ConsentSubject.Should().BeSameAs(user); _mockConsent.ConsentScopes.Should().BeEquivalentTo(scopes); } private static IEnumerable GetIdentityScopes() { return new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile(), new IdentityResources.Email() }; } private static IEnumerable GetApiResources() { return new ApiResource[] { new ApiResource { Name = "api", Scopes = { "read", "write", "forbidden" } } }; } private static IEnumerable GetScopes() { return new ApiScope[] { new ApiScope { Name = "read", DisplayName = "Read data", Emphasize = false }, new ApiScope { Name = "write", DisplayName = "Write data", Emphasize = true }, new ApiScope { Name = "forbidden", DisplayName = "Forbidden scope", Emphasize = true } }; } public AuthorizeInteractionResponseGeneratorTests_Consent() { _subject = new IdentityServer8.ResponseHandling.AuthorizeInteractionResponseGenerator( new StubClock(), TestLogger.Create(), _mockConsent, _fakeUserService); } private static ResourceValidationResult GetValidatedResources(params string[] scopes) { var resources = new Resources(GetIdentityScopes(), GetApiResources(), GetScopes()); return new ResourceValidationResult(resources).Filter(scopes); } [Fact] public async Task ProcessConsentAsync_NullRequest_Throws() { Func act = async () => await _subject.ProcessConsentAsync(null, new ConsentResponse()); (await act.Should().ThrowAsync()) .And.ParamName.Should().Be("request"); } [Fact] public async Task ProcessConsentAsync_AllowsNullConsent() { var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", PromptModes = new[] { OidcConstants.PromptModes.Consent }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; await _subject.ProcessConsentAsync(request, null); } [Fact] public async Task ProcessConsentAsync_PromptModeIsLogin_Throws() { RequiresConsent(true); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", PromptModes = new[] { OidcConstants.PromptModes.Login }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; Func act = async () => await _subject.ProcessConsentAsync(request); (await act.Should().ThrowAsync()) .And.Message.Should().Contain("PromptMode"); } [Fact] public async Task ProcessConsentAsync_PromptModeIsSelectAccount_Throws() { RequiresConsent(true); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", PromptModes = new[] { OidcConstants.PromptModes.SelectAccount }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; Func act = async () => await _subject.ProcessConsentAsync(request); (await act.Should().ThrowAsync()) .And.Message.Should().Contain("PromptMode"); } [Fact] public async Task ProcessConsentAsync_RequiresConsentButPromptModeIsNone_ReturnsErrorResult() { RequiresConsent(true); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", PromptModes = new[] { OidcConstants.PromptModes.None }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var result = await _subject.ProcessConsentAsync(request); request.WasConsentShown.Should().BeFalse(); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.ConsentRequired); AssertUpdateConsentNotCalled(); } [Fact] public async Task ProcessConsentAsync_PromptModeIsConsent_NoPriorConsent_ReturnsConsentResult() { var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", PromptModes = new[] { OidcConstants.PromptModes.Consent }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var result = await _subject.ProcessConsentAsync(request); request.WasConsentShown.Should().BeFalse(); result.IsConsent.Should().BeTrue(); AssertUpdateConsentNotCalled(); } [Fact] public async Task ProcessConsentAsync_NoPromptMode_ConsentServiceRequiresConsent_NoPriorConsent_ReturnsConsentResult() { RequiresConsent(true); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", PromptModes = new[] { OidcConstants.PromptModes.Consent }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var result = await _subject.ProcessConsentAsync(request); request.WasConsentShown.Should().BeFalse(); result.IsConsent.Should().BeTrue(); AssertUpdateConsentNotCalled(); } [Fact] public async Task ProcessConsentAsync_PromptModeIsConsent_ConsentNotGranted_ReturnsErrorResult() { var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", PromptModes = new[] { OidcConstants.PromptModes.Consent }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var consent = new ConsentResponse { RememberConsent = false, ScopesValuesConsented = new string[] { } }; var result = await _subject.ProcessConsentAsync(request, consent); request.WasConsentShown.Should().BeTrue(); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.AccessDenied); AssertUpdateConsentNotCalled(); } [Fact] public async Task ProcessConsentAsync_NoPromptMode_ConsentServiceRequiresConsent_ConsentNotGranted_ReturnsErrorResult() { RequiresConsent(true); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var consent = new ConsentResponse { RememberConsent = false, ScopesValuesConsented = new string[] { } }; var result = await _subject.ProcessConsentAsync(request, consent); request.WasConsentShown.Should().BeTrue(); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.AccessDenied); AssertUpdateConsentNotCalled(); } [Fact] public async Task ProcessConsentAsync_NoPromptMode_ConsentServiceRequiresConsent_ConsentGrantedButMissingRequiredScopes_ReturnsErrorResult() { RequiresConsent(true); var client = new Client { }; var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), Client = client }; var consent = new ConsentResponse { RememberConsent = false, ScopesValuesConsented = new string[] { "read" } }; var result = await _subject.ProcessConsentAsync(request, consent); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.AccessDenied); AssertUpdateConsentNotCalled(); } [Fact] public async Task ProcessConsentAsync_NoPromptMode_ConsentServiceRequiresConsent_ConsentGranted_ScopesSelected_ReturnsConsentResult() { RequiresConsent(true); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", Client = new Client { AllowRememberConsent = false }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var consent = new ConsentResponse { RememberConsent = false, ScopesValuesConsented = new string[] { "openid", "read" } }; var result = await _subject.ProcessConsentAsync(request, consent); request.ValidatedResources.Resources.IdentityResources.Count().Should().Be(1); request.ValidatedResources.Resources.ApiScopes.Count().Should().Be(1); "openid".Should().Be(request.ValidatedResources.Resources.IdentityResources.Select(x => x.Name).First()); "read".Should().Be(request.ValidatedResources.Resources.ApiScopes.First().Name); request.WasConsentShown.Should().BeTrue(); result.IsConsent.Should().BeFalse(); AssertUpdateConsentNotCalled(); } [Fact] public async Task ProcessConsentAsync_PromptModeConsent_ConsentGranted_ScopesSelected_ReturnsConsentResult() { RequiresConsent(true); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", Client = new Client { AllowRememberConsent = false }, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var consent = new ConsentResponse { RememberConsent = false, ScopesValuesConsented = new string[] { "openid", "read" } }; var result = await _subject.ProcessConsentAsync(request, consent); request.ValidatedResources.Resources.IdentityResources.Count().Should().Be(1); request.ValidatedResources.Resources.ApiScopes.Count().Should().Be(1); "read".Should().Be(request.ValidatedResources.Resources.ApiScopes.First().Name); request.WasConsentShown.Should().BeTrue(); result.IsConsent.Should().BeFalse(); AssertUpdateConsentNotCalled(); } [Fact] public async Task ProcessConsentAsync_AllowConsentSelected_SavesConsent() { RequiresConsent(true); var client = new Client { AllowRememberConsent = true }; var user = new ClaimsPrincipal(); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", Client = client, Subject = user, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var consent = new ConsentResponse { RememberConsent = true, ScopesValuesConsented = new string[] { "openid", "read" } }; var result = await _subject.ProcessConsentAsync(request, consent); AssertUpdateConsentCalled(client, user, "openid", "read"); } [Fact] public async Task ProcessConsentAsync_NotRememberingConsent_DoesNotSaveConsent() { RequiresConsent(true); var client = new Client { AllowRememberConsent = true }; var user = new ClaimsPrincipal(); var request = new ValidatedAuthorizeRequest() { ResponseMode = OidcConstants.ResponseModes.Fragment, State = "12345", RedirectUri = "https://client.com/callback", Client = client, Subject = user, RequestedScopes = new List { "openid", "read", "write" }, ValidatedResources = GetValidatedResources("openid", "read", "write"), }; var consent = new ConsentResponse { RememberConsent = false, ScopesValuesConsented = new string[] { "openid", "read" } }; var result = await _subject.ProcessConsentAsync(request, consent); AssertUpdateConsentCalled(client, user); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/ResponseHandling/AuthorizeInteractionResponseGenerator/AuthorizeInteractionResponseGeneratorTests_Custom.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.ResponseHandling; using IdentityServer8.Services; using IdentityServer8.Validation; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; using Xunit; using static IdentityModel.OidcConstants; namespace IdentityServer.UnitTests.ResponseHandling.AuthorizeInteractionResponseGenerator { public class CustomAuthorizeInteractionResponseGenerator : IdentityServer8.ResponseHandling.AuthorizeInteractionResponseGenerator { public CustomAuthorizeInteractionResponseGenerator(ISystemClock clock, ILogger logger, IConsentService consent, IProfileService profile) : base(clock, logger, consent, profile) { } public InteractionResponse ProcessLoginResponse { get; set; } protected internal override Task ProcessLoginAsync(ValidatedAuthorizeRequest request) { if (ProcessLoginResponse != null) { return Task.FromResult(ProcessLoginResponse); } return base.ProcessLoginAsync(request); } public InteractionResponse ProcessConsentResponse { get; set; } protected internal override Task ProcessConsentAsync(ValidatedAuthorizeRequest request, ConsentResponse consent = null) { if (ProcessConsentResponse != null) { return Task.FromResult(ProcessConsentResponse); } return base.ProcessConsentAsync(request, consent); } } public class AuthorizeInteractionResponseGeneratorTests_Custom { private IdentityServerOptions _options = new IdentityServerOptions(); private CustomAuthorizeInteractionResponseGenerator _subject; private MockConsentService _mockConsentService = new MockConsentService(); private StubClock _clock = new StubClock(); public AuthorizeInteractionResponseGeneratorTests_Custom() { _subject = new CustomAuthorizeInteractionResponseGenerator( _clock, TestLogger.Create(), _mockConsentService, new MockProfileService()); } [Fact] public async Task ProcessInteractionAsync_with_overridden_login_returns_redirect_should_return_redirect() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), Client = new Client { }, }; _subject.ProcessLoginResponse = new InteractionResponse { RedirectUrl = "/custom" }; var result = await _subject.ProcessInteractionAsync(request); result.IsRedirect.Should().BeTrue(); result.RedirectUrl.Should().Be("/custom"); } [Fact] public async Task ProcessInteractionAsync_with_prompt_none_and_login_returns_login_should_return_error() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), Client = new Client { }, PromptModes = new[] { PromptModes.None }, }; _subject.ProcessLoginResponse = new InteractionResponse { IsLogin = true }; var result = await _subject.ProcessInteractionAsync(request); result.IsError.Should().BeTrue(); result.Error.Should().Be("login_required"); } [Fact] public async Task ProcessInteractionAsync_with_prompt_none_and_login_returns_redirect_should_return_error() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), Client = new Client { }, PromptModes = new[] { PromptModes.None }, }; _subject.ProcessLoginResponse = new InteractionResponse { RedirectUrl = "/custom" }; var result = await _subject.ProcessInteractionAsync(request); result.IsError.Should().BeTrue(); result.Error.Should().Be("interaction_required"); result.RedirectUrl.Should().BeNull(); } [Fact] public async Task ProcessInteractionAsync_with_prompt_none_and_consent_returns_consent_should_return_error() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), Client = new Client { }, PromptModes = new[] { PromptModes.None }, }; _subject.ProcessConsentResponse = new InteractionResponse { IsConsent = true }; var result = await _subject.ProcessInteractionAsync(request); result.IsError.Should().BeTrue(); result.Error.Should().Be("consent_required"); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/ResponseHandling/AuthorizeInteractionResponseGenerator/AuthorizeInteractionResponseGeneratorTests_Login.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.ResponseHandling.AuthorizeInteractionResponseGenerator { public class AuthorizeInteractionResponseGeneratorTests_Login { private IdentityServerOptions _options = new IdentityServerOptions(); private IdentityServer8.ResponseHandling.AuthorizeInteractionResponseGenerator _subject; private MockConsentService _mockConsentService = new MockConsentService(); private StubClock _clock = new StubClock(); public AuthorizeInteractionResponseGeneratorTests_Login() { _subject = new IdentityServer8.ResponseHandling.AuthorizeInteractionResponseGenerator( _clock, TestLogger.Create(), _mockConsentService, new MockProfileService()); } [Fact] public async Task Anonymous_User_must_SignIn() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = Principal.Anonymous }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeTrue(); } [Fact] public async Task Authenticated_User_must_not_SignIn() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client(), ValidatedResources = new ResourceValidationResult(), Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal() }; var result = await _subject.ProcessInteractionAsync(request); result.IsLogin.Should().BeFalse(); } [Fact] public async Task Authenticated_User_with_allowed_current_Idp_must_not_SignIn() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), Client = new Client { IdentityProviderRestrictions = new List { IdentityServerConstants.LocalIdentityProvider } } }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeFalse(); } [Fact] public async Task Authenticated_User_with_restricted_current_Idp_must_SignIn() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal(), Client = new Client { EnableLocalLogin = false, IdentityProviderRestrictions = new List { "some_idp" } } }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeTrue(); } [Fact] public async Task Authenticated_User_with_allowed_requested_Idp_must_not_SignIn() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client(), AuthenticationContextReferenceClasses = new List{ "idp:" + IdentityServerConstants.LocalIdentityProvider }, Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal() }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeFalse(); } [Fact] public async Task Authenticated_User_with_different_requested_Idp_must_SignIn() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client(), AuthenticationContextReferenceClasses = new List{ "idp:some_idp" }, Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal() }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeTrue(); } [Fact] public async Task Authenticated_User_within_client_user_sso_lifetime_should_not_signin() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client() { UserSsoLifetime = 3600 // 1h }, Subject = new IdentityServerUser("123") { IdentityProvider = "local", AuthenticationTime = _clock.UtcNow.UtcDateTime.Subtract(TimeSpan.FromSeconds(10)) }.CreatePrincipal() }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeFalse(); } [Fact] public async Task Authenticated_User_beyond_client_user_sso_lifetime_should_signin() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client() { UserSsoLifetime = 3600 // 1h }, Subject = new IdentityServerUser("123") { IdentityProvider = "local", AuthenticationTime = _clock.UtcNow.UtcDateTime.Subtract(TimeSpan.FromSeconds(3700)) }.CreatePrincipal() }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeTrue(); } [Fact] public async Task locally_authenticated_user_but_client_does_not_allow_local_should_sign_in() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Client = new Client() { EnableLocalLogin = false }, Subject = new IdentityServerUser("123") { IdentityProvider = IdentityServerConstants.LocalIdentityProvider }.CreatePrincipal() }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeTrue(); } [Fact] public async Task prompt_login_should_sign_in() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123").CreatePrincipal(), PromptModes = new[] { OidcConstants.PromptModes.Login }, Raw = new NameValueCollection() }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeTrue(); } [Fact] public async Task prompt_select_account_should_sign_in() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123").CreatePrincipal(), PromptModes = new[] { OidcConstants.PromptModes.SelectAccount }, Raw = new NameValueCollection() }; var result = await _subject.ProcessLoginAsync(request); result.IsLogin.Should().BeTrue(); } [Fact] public async Task prompt_for_signin_should_remove_prompt_from_raw_url() { var request = new ValidatedAuthorizeRequest { ClientId = "foo", Subject = new IdentityServerUser("123").CreatePrincipal(), PromptModes = new[] { OidcConstants.PromptModes.Login }, Raw = new NameValueCollection { { OidcConstants.AuthorizeRequest.Prompt, OidcConstants.PromptModes.Login } } }; var result = await _subject.ProcessLoginAsync(request); request.Raw.AllKeys.Should().NotContain(OidcConstants.AuthorizeRequest.Prompt); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/ResponseHandling/DeviceAuthorizationResponseGeneratorTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.ResponseHandling; using IdentityServer8.Services; using IdentityServer8.Services.Default; using IdentityServer8.Stores; using IdentityServer8.Validation; using Microsoft.Extensions.Logging.Abstractions; using Xunit; namespace IdentityServer.UnitTests.ResponseHandling { public class DeviceAuthorizationResponseGeneratorTests { private readonly List identityResources = new List {new IdentityResources.OpenId(), new IdentityResources.Profile()}; private readonly List apiResources = new List { new ApiResource("resource") { Scopes = {"api1" } } }; private readonly List scopes = new List { new ApiScope("api1") }; private readonly FakeUserCodeGenerator fakeUserCodeGenerator = new FakeUserCodeGenerator(); private readonly IDeviceFlowCodeService deviceFlowCodeService = new DefaultDeviceFlowCodeService(new InMemoryDeviceFlowStore(), new StubHandleGenerationService()); private readonly IdentityServerOptions options = new IdentityServerOptions(); private readonly StubClock clock = new StubClock(); private readonly DeviceAuthorizationResponseGenerator generator; private readonly DeviceAuthorizationRequestValidationResult testResult; private const string TestBaseUrl = "http://localhost:5000/"; public DeviceAuthorizationResponseGeneratorTests() { testResult = new DeviceAuthorizationRequestValidationResult(new ValidatedDeviceAuthorizationRequest { Client = new Client {ClientId = Guid.NewGuid().ToString()}, IsOpenIdRequest = true, ValidatedResources = new ResourceValidationResult() }); generator = new DeviceAuthorizationResponseGenerator( options, new DefaultUserCodeService(new IUserCodeGenerator[] {new NumericUserCodeGenerator(), fakeUserCodeGenerator }), deviceFlowCodeService, clock, new NullLogger()); } [Fact] public async Task ProcessAsync_when_valiationresult_null_exect_exception() { Func act = async () => await generator.ProcessAsync(null, TestBaseUrl); await act.Should().ThrowAsync(); } [Fact] public void ProcessAsync_when_valiationresult_client_null_exect_exception() { var validationResult = new DeviceAuthorizationRequestValidationResult(new ValidatedDeviceAuthorizationRequest()); Func act = () => generator.ProcessAsync(validationResult, TestBaseUrl); act.Should().ThrowAsync(); } [Fact] public void ProcessAsync_when_baseurl_null_exect_exception() { Func act = () => generator.ProcessAsync(testResult, null); act.Should().ThrowAsync(); } [Fact] public async Task ProcessAsync_when_user_code_collision_expect_retry() { var creationTime = DateTime.UtcNow; clock.UtcNowFunc = () => creationTime; testResult.ValidatedRequest.Client.UserCodeType = FakeUserCodeGenerator.UserCodeTypeValue; await deviceFlowCodeService.StoreDeviceAuthorizationAsync(FakeUserCodeGenerator.TestCollisionUserCode, new DeviceCode()); var response = await generator.ProcessAsync(testResult, TestBaseUrl); response.UserCode.Should().Be(FakeUserCodeGenerator.TestUniqueUserCode); } [Fact] public async Task ProcessAsync_when_user_code_collision_retry_limit_reached_expect_error() { var creationTime = DateTime.UtcNow; clock.UtcNowFunc = () => creationTime; fakeUserCodeGenerator.RetryLimit = 1; testResult.ValidatedRequest.Client.UserCodeType = FakeUserCodeGenerator.UserCodeTypeValue; await deviceFlowCodeService.StoreDeviceAuthorizationAsync(FakeUserCodeGenerator.TestCollisionUserCode, new DeviceCode()); await Assert.ThrowsAsync(() => generator.ProcessAsync(testResult, TestBaseUrl)); } [Fact] public async Task ProcessAsync_when_generated_expect_user_code_stored() { var creationTime = DateTime.UtcNow; clock.UtcNowFunc = () => creationTime; testResult.ValidatedRequest.RequestedScopes = new List { "openid", "api1" }; testResult.ValidatedRequest.ValidatedResources = new ResourceValidationResult(new Resources( identityResources.Where(x=>x.Name == "openid"), apiResources.Where(x=>x.Name == "resource"), scopes.Where(x=>x.Name == "api1"))); var response = await generator.ProcessAsync(testResult, TestBaseUrl); response.UserCode.Should().NotBeNullOrWhiteSpace(); var userCode = await deviceFlowCodeService.FindByUserCodeAsync(response.UserCode); userCode.Should().NotBeNull(); userCode.ClientId.Should().Be(testResult.ValidatedRequest.Client.ClientId); userCode.Lifetime.Should().Be(testResult.ValidatedRequest.Client.DeviceCodeLifetime); userCode.CreationTime.Should().Be(creationTime); userCode.Subject.Should().BeNull(); userCode.AuthorizedScopes.Should().BeNull(); userCode.RequestedScopes.Should().Contain(testResult.ValidatedRequest.RequestedScopes); } [Fact] public async Task ProcessAsync_when_generated_expect_device_code_stored() { var creationTime = DateTime.UtcNow; clock.UtcNowFunc = () => creationTime; var response = await generator.ProcessAsync(testResult, TestBaseUrl); response.DeviceCode.Should().NotBeNullOrWhiteSpace(); response.Interval.Should().Be(options.DeviceFlow.Interval); var deviceCode = await deviceFlowCodeService.FindByDeviceCodeAsync(response.DeviceCode); deviceCode.Should().NotBeNull(); deviceCode.ClientId.Should().Be(testResult.ValidatedRequest.Client.ClientId); deviceCode.IsOpenId.Should().Be(testResult.ValidatedRequest.IsOpenIdRequest); deviceCode.Lifetime.Should().Be(testResult.ValidatedRequest.Client.DeviceCodeLifetime); deviceCode.CreationTime.Should().Be(creationTime); deviceCode.Subject.Should().BeNull(); deviceCode.AuthorizedScopes.Should().BeNull(); response.DeviceCodeLifetime.Should().Be(deviceCode.Lifetime); } [Fact] public async Task ProcessAsync_when_DeviceVerificationUrl_is_relative_uri_expect_correct_VerificationUris() { const string baseUrl = "http://localhost:5000/"; options.UserInteraction.DeviceVerificationUrl = "/device"; options.UserInteraction.DeviceVerificationUserCodeParameter = "userCode"; var response = await generator.ProcessAsync(testResult, baseUrl); response.VerificationUri.Should().Be("http://localhost:5000/device"); response.VerificationUriComplete.Should().StartWith("http://localhost:5000/device?userCode="); } [Fact] public async Task ProcessAsync_when_DeviceVerificationUrl_is_absolute_uri_expect_correct_VerificationUris() { const string baseUrl = "http://localhost:5000/"; options.UserInteraction.DeviceVerificationUrl = "http://short/device"; options.UserInteraction.DeviceVerificationUserCodeParameter = "userCode"; var response = await generator.ProcessAsync(testResult, baseUrl); response.VerificationUri.Should().Be("http://short/device"); response.VerificationUriComplete.Should().StartWith("http://short/device?userCode="); } } internal class FakeUserCodeGenerator : IUserCodeGenerator { public const string UserCodeTypeValue = "Collider"; public const string TestUniqueUserCode = "123"; public const string TestCollisionUserCode = "321"; private int tryCount = 0; private int retryLimit = 2; public string UserCodeType => UserCodeTypeValue; public int RetryLimit { get => retryLimit; set => retryLimit = value; } public Task GenerateAsync() { if (tryCount == 0) { tryCount++; return Task.FromResult(TestCollisionUserCode); } tryCount++; return Task.FromResult(TestUniqueUserCode); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/ResponseHandling/UserInfoResponseGeneratorTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Security.Claims; using System.Text.Json; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.ResponseHandling; using IdentityServer8.Stores; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.ResponseHandling { public class UserInfoResponseGeneratorTests { private UserInfoResponseGenerator _subject; private MockProfileService _mockProfileService = new MockProfileService(); private ClaimsPrincipal _user; private Client _client; private InMemoryResourcesStore _resourceStore; private List _identityResources = new List(); private List _apiResources = new List(); private List _apiScopes = new List(); public UserInfoResponseGeneratorTests() { _client = new Client { ClientId = "client" }; _user = new IdentityServerUser("bob") { AdditionalClaims = { new Claim("foo", "foo1"), new Claim("foo", "foo2"), new Claim("bar", "bar1"), new Claim("bar", "bar2") } }.CreatePrincipal(); _resourceStore = new InMemoryResourcesStore(_identityResources, _apiResources, _apiScopes); _subject = new UserInfoResponseGenerator(_mockProfileService, _resourceStore, TestLogger.Create()); } [Fact] public async Task GetRequestedClaimTypesAsync_when_no_scopes_requested_should_return_empty_claim_types() { var resources = await _subject.GetRequestedResourcesAsync(null); var claims = await _subject.GetRequestedClaimTypesAsync(resources); claims.Should().BeEquivalentTo(new string[] { }); } [Fact] public async Task GetRequestedClaimTypesAsync_should_return_correct_identity_claims() { _identityResources.Add(new IdentityResource("id1", new[] { "c1", "c2" })); _identityResources.Add(new IdentityResource("id2", new[] { "c2", "c3" })); var resources = await _subject.GetRequestedResourcesAsync(new[] { "id1", "id2", "id3" }); var claims = await _subject.GetRequestedClaimTypesAsync(resources); claims.Should().BeEquivalentTo(new string[] { "c1", "c2", "c3" }); } [Fact] public async Task GetRequestedClaimTypesAsync_should_only_return_enabled_identity_claims() { _identityResources.Add(new IdentityResource("id1", new[] { "c1", "c2" }) { Enabled = false }); _identityResources.Add(new IdentityResource("id2", new[] { "c2", "c3" })); var resources = await _subject.GetRequestedResourcesAsync(new[] { "id1", "id2", "id3" }); var claims = await _subject.GetRequestedClaimTypesAsync(resources); claims.Should().BeEquivalentTo(new string[] { "c2", "c3" }); } [Fact] public async Task ProcessAsync_should_call_profile_service_with_requested_claim_types() { _identityResources.Add(new IdentityResource("id1", new[] { "foo" })); _identityResources.Add(new IdentityResource("id2", new[] { "bar" })); var result = new UserInfoRequestValidationResult { Subject = _user, TokenValidationResult = new TokenValidationResult { Claims = new List { { new Claim("scope", "id1") }, { new Claim("scope", "id2") }, { new Claim("scope", "id3") } }, Client = _client } }; var claims = await _subject.ProcessAsync(result); _mockProfileService.GetProfileWasCalled.Should().BeTrue(); _mockProfileService.ProfileContext.RequestedClaimTypes.Should().BeEquivalentTo(new[] { "foo", "bar" }); } [Fact] public async Task ProcessAsync_should_return_claims_issued_by_profile_service() { _identityResources.Add(new IdentityResource("id1", new[] { "foo" })); _identityResources.Add(new IdentityResource("id2", new[] { "bar" })); var address = new { street_address = "One Hacker Way", locality = "Heidelberg", postal_code = 69118, country = "Germany" }; _mockProfileService.ProfileClaims = new[] { new Claim("email", "fred@gmail.com"), new Claim("name", "fred jones"), new Claim("address", @"{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }", IdentityServerConstants.ClaimValueTypes.Json), new Claim("address2", JsonSerializer.Serialize(address), IdentityServerConstants.ClaimValueTypes.Json) }; var result = new UserInfoRequestValidationResult { Subject = _user, TokenValidationResult = new TokenValidationResult { Claims = new List { { new Claim("scope", "id1") }, { new Claim("scope", "id2") }, { new Claim("scope", "id3") } }, Client = _client } }; var claims = await _subject.ProcessAsync(result); claims.Should().ContainKey("email"); claims["email"].Should().Be("fred@gmail.com"); claims.Should().ContainKey("name"); claims["name"].Should().Be("fred jones"); // this will be treated as a string because this is not valid JSON from the System.Text library point of view claims.Should().ContainKey("address"); claims["address"].Should().Be("{ 'street_address': 'One Hacker Way', 'locality': 'Heidelberg', 'postal_code': 69118, 'country': 'Germany' }"); // this is a JsonElement claims.Should().ContainKey("address2"); claims["address2"].ToString().Should().Be("{\"street_address\":\"One Hacker Way\",\"locality\":\"Heidelberg\",\"postal_code\":69118,\"country\":\"Germany\"}"); } [Fact] public async Task ProcessAsync_should_return_sub_from_user() { _identityResources.Add(new IdentityResource("id1", new[] { "foo" })); _identityResources.Add(new IdentityResource("id2", new[] { "bar" })); var result = new UserInfoRequestValidationResult { Subject = _user, TokenValidationResult = new TokenValidationResult { Claims = new List { { new Claim("scope", "id1") }, { new Claim("scope", "id2") }, { new Claim("scope", "id3") } }, Client = _client } }; var claims = await _subject.ProcessAsync(result); claims.Should().ContainKey("sub"); claims["sub"].Should().Be("bob"); } [Fact] public async Task ProcessAsync_should_throw_if_incorrect_sub_issued_by_profile_service() { _identityResources.Add(new IdentityResource("id1", new[] { "foo" })); _identityResources.Add(new IdentityResource("id2", new[] { "bar" })); _mockProfileService.ProfileClaims = new[] { new Claim("sub", "fred") }; var result = new UserInfoRequestValidationResult { Subject = _user, TokenValidationResult = new TokenValidationResult { Claims = new List { { new Claim("scope", "id1") }, { new Claim("scope", "id2") }, { new Claim("scope", "id3") } }, Client = _client } }; Func act = async () => await _subject.ProcessAsync(result); (await act.Should().ThrowAsync()) .And.Message.Should().Contain("subject"); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DefaultClaimsServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DefaultClaimsServiceTests { private DefaultClaimsService _subject; private MockProfileService _mockMockProfileService = new MockProfileService(); private ClaimsPrincipal _user; private Client _client; private ValidatedRequest _validatedRequest; private Resources _resources = new Resources(); public ResourceValidationResult ResourceValidationResult => new ResourceValidationResult(_resources); public DefaultClaimsServiceTests() { _client = new Client { ClientId = "client", Claims = { new ClientClaim("some_claim", "some_claim_value") } }; _user = new IdentityServerUser("bob") { IdentityProvider = "idp", AuthenticationMethods = { OidcConstants.AuthenticationMethods.Password }, AuthenticationTime = new System.DateTime(2000, 1, 1), AdditionalClaims = { new Claim("foo", "foo1"), new Claim("foo", "foo2"), new Claim("bar", "bar1"), new Claim("bar", "bar2"), new Claim(JwtClaimTypes.AuthenticationContextClassReference, "acr1") } }.CreatePrincipal(); _subject = new DefaultClaimsService(_mockMockProfileService, TestLogger.Create()); _validatedRequest = new ValidatedRequest(); _validatedRequest.Options = new IdentityServerOptions(); _validatedRequest.SetClient(_client); } [Fact] public async Task GetIdentityTokenClaimsAsync_should_return_standard_user_claims() { var claims = await _subject.GetIdentityTokenClaimsAsync(_user, ResourceValidationResult, false, _validatedRequest); var types = claims.Select(x => x.Type); types.Should().Contain(JwtClaimTypes.Subject); types.Should().Contain(JwtClaimTypes.AuthenticationTime); types.Should().Contain(JwtClaimTypes.IdentityProvider); types.Should().Contain(JwtClaimTypes.AuthenticationMethod); types.Should().Contain(JwtClaimTypes.AuthenticationContextClassReference); } [Fact] public async Task GetIdentityTokenClaimsAsync_should_return_minimal_claims_when_includeAllIdentityClaims_is_false() { _resources.IdentityResources.Add(new IdentityResource("id_scope", new[] { "foo" })); var claims = await _subject.GetIdentityTokenClaimsAsync(_user, ResourceValidationResult, false, _validatedRequest); _mockMockProfileService.GetProfileWasCalled.Should().BeFalse(); } [Fact] public async Task GetIdentityTokenClaimsAsync_should_return_all_claims_when_includeAllIdentityClaims_is_true() { _resources.IdentityResources.Add(new IdentityResource("id_scope", new[] { "foo" })); _mockMockProfileService.ProfileClaims.Add(new Claim("foo", "foo1")); var claims = await _subject.GetIdentityTokenClaimsAsync(_user, ResourceValidationResult, true, _validatedRequest); _mockMockProfileService.GetProfileWasCalled.Should().BeTrue(); _mockMockProfileService.ProfileContext.RequestedClaimTypes.Should().Contain("foo"); } [Fact] public async Task GetIdentityTokenClaimsAsync_should_return_all_claims_when_client_configured_for_always_include_all_claims_in_id_token() { _client.AlwaysIncludeUserClaimsInIdToken = true; _resources.IdentityResources.Add(new IdentityResource("id_scope", new[] { "foo" })); _mockMockProfileService.ProfileClaims.Add(new Claim("foo", "foo1")); var claims = await _subject.GetIdentityTokenClaimsAsync(_user, ResourceValidationResult, false, _validatedRequest); _mockMockProfileService.GetProfileWasCalled.Should().BeTrue(); _mockMockProfileService.ProfileContext.RequestedClaimTypes.Should().Contain("foo"); } [Fact] public async Task GetIdentityTokenClaimsAsync_should_filter_protocol_claims_from_profile_service() { _resources.IdentityResources.Add(new IdentityResource("id_scope", new[] { "foo" })); _mockMockProfileService.ProfileClaims.Add(new Claim("aud", "bar")); var claims = await _subject.GetIdentityTokenClaimsAsync(_user, ResourceValidationResult, true, _validatedRequest); claims.Count(x => x.Type == "aud" && x.Value == "bar").Should().Be(0); } [Fact] public async Task GetAccessTokenClaimsAsync_should_contain_client_id() { var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); claims.Count(x => x.Type == JwtClaimTypes.ClientId && x.Value == _client.ClientId).Should().Be(1); } [Fact] public async Task GetAccessTokenClaimsAsync_client_claims_should_be_prefixed_with_default_value() { var claims = await _subject.GetAccessTokenClaimsAsync(null, ResourceValidationResult, _validatedRequest); claims.Count(x => x.Type == "client_some_claim" && x.Value == "some_claim_value").Should().Be(1); } [Fact] public async Task GetAccessTokenClaimsAsync_client_claims_should_be_prefixed_with_custom_value() { _validatedRequest.Client.ClientClaimsPrefix = "custom_prefix_"; var claims = await _subject.GetAccessTokenClaimsAsync(null, ResourceValidationResult, _validatedRequest); claims.Count(x => x.Type == "custom_prefix_some_claim" && x.Value == "some_claim_value").Should().Be(1); } [Fact] public async Task GetAccessTokenClaimsAsync_should_contain_client_claims_when_no_subject() { _validatedRequest.Client.ClientClaimsPrefix = null; var claims = await _subject.GetAccessTokenClaimsAsync(null, ResourceValidationResult, _validatedRequest); claims.Count(x => x.Type == "some_claim" && x.Value == "some_claim_value").Should().Be(1); } [Fact] public async Task GetAccessTokenClaimsAsync_should_contain_client_claims_when_configured_to_send_client_claims() { _validatedRequest.Client.ClientClaimsPrefix = null; _validatedRequest.Client.AlwaysSendClientClaims = true; var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); claims.Count(x => x.Type == "some_claim" && x.Value == "some_claim_value").Should().Be(1); } [Fact] public async Task GetAccessTokenClaimsAsync_should_contain_scopes() { _resources.IdentityResources.Add(new IdentityResource("id1", new[] { "foo" })); _resources.IdentityResources.Add(new IdentityResource("id2", new[] { "bar" })); _resources.ApiScopes.Add(new ApiScope("api1")); _resources.ApiScopes.Add(new ApiScope("api2")); var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Count().Should().Be(4); scopes.ToArray().Should().BeEquivalentTo(new string[] { "api1", "api2", "id1", "id2" }); } [Fact] public async Task GetAccessTokenClaimsAsync_should_contain_parameterized_scope_values() { _resources.ApiScopes.Add(new ApiScope("api")); var resourceResult = new ResourceValidationResult() { Resources = _resources, ParsedScopes = { new ParsedScopeValue("api:123", "api", "123") } }; var claims = await _subject.GetAccessTokenClaimsAsync(_user, resourceResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Count().Should().Be(1); scopes.ToArray().Should().BeEquivalentTo(new string[] { "api:123" }); } [Fact] public async Task GetAccessTokenClaimsAsync_when_no_ApiScopes_should_not_contain_scopes() { _resources.ApiResources.Add(new ApiResource("api1")); var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Count().Should().Be(0); } [Fact] public async Task GetAccessTokenClaimsAsync_should_only_consider_parsed_scope_values_and_not_ApiScope() { // arguably, if this situation arises, then the ResourceValidationResult was not populated properly // with ParsedScopes matching ApiScopes _resources.ApiScopes.Add(new ApiScope("api1")); var resourceResult = new ResourceValidationResult() { Resources = _resources, ParsedScopes = { new ParsedScopeValue("api2") } }; var claims = await _subject.GetAccessTokenClaimsAsync(_user, resourceResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Count().Should().Be(1); scopes.ToArray().Should().BeEquivalentTo(new string[] { "api2" }); } [Fact] public async Task GetAccessTokenClaimsAsync_when_multiple_resources_with_same_scope_should_contain_scope_once() { _resources.OfflineAccess = false; _resources.IdentityResources.Clear(); _resources.ApiResources.Clear(); _resources.ApiScopes.Clear(); _resources.ApiResources.Add(new ApiResource { Name = "api1", Scopes = { "resource" } }); _resources.ApiResources.Add(new ApiResource { Name = "api2", Scopes = { "resource" } }); _resources.ApiResources.Add(new ApiResource { Name = "api3", Scopes = { "resource" } }); _resources.ApiScopes.Add(new ApiScope("resource")); var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Count().Should().Be(1); scopes.ToArray().Should().BeEquivalentTo(new string[] { "resource" }); } [Fact] public async Task GetAccessTokenClaimsAsync_should_contain_offline_scope() { _resources.IdentityResources.Add(new IdentityResource("id1", new[] { "foo" })); _resources.IdentityResources.Add(new IdentityResource("id2", new[] { "bar" })); _resources.ApiResources.Add(new ApiResource("api1")); _resources.ApiResources.Add(new ApiResource("api2")); _resources.OfflineAccess = true; var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Should().Contain(IdentityServerConstants.StandardScopes.OfflineAccess); } [Fact] public async Task GetAccessTokenClaimsAsync_should_not_contain_offline_scope_if_no_user() { _resources.IdentityResources.Add(new IdentityResource("id1", new[] { "foo" })); _resources.IdentityResources.Add(new IdentityResource("id2", new[] { "bar" })); _resources.ApiResources.Add(new ApiResource("api1")); _resources.ApiResources.Add(new ApiResource("api2")); _resources.OfflineAccess = true; var claims = await _subject.GetAccessTokenClaimsAsync(null, ResourceValidationResult, _validatedRequest); var scopes = claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); scopes.Should().NotContain(IdentityServerConstants.StandardScopes.OfflineAccess); } [Fact] public async Task GetAccessTokenClaimsAsync_should_return_standard_user_claims() { var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); var types = claims.Select(x => x.Type); types.Should().Contain(JwtClaimTypes.Subject); types.Should().Contain(JwtClaimTypes.AuthenticationTime); types.Should().Contain(JwtClaimTypes.IdentityProvider); types.Should().Contain(JwtClaimTypes.AuthenticationMethod); types.Should().Contain(JwtClaimTypes.AuthenticationContextClassReference); } [Fact] public async Task GetAccessTokenClaimsAsync_should_only_contain_api_claims() { _resources.IdentityResources.Add(new IdentityResource("id1", new[] { "foo" })); _resources.ApiResources.Add(new ApiResource("api1", new string[] { "bar" })); var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); _mockMockProfileService.GetProfileWasCalled.Should().BeTrue(); _mockMockProfileService.ProfileContext.RequestedClaimTypes.Should().NotContain("foo"); _mockMockProfileService.ProfileContext.RequestedClaimTypes.Should().Contain("bar"); } [Fact] public async Task GetAccessTokenClaimsAsync_should_filter_protocol_claims_from_profile_service() { _resources.ApiResources.Add(new ApiResource("api1", new[] { "foo" })); _mockMockProfileService.ProfileClaims.Add(new Claim("aud", "bar")); var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); claims.Count(x => x.Type == "aud" && x.Value == "bar").Should().Be(0); } [Fact] public async Task GetAccessTokenClaimsAsync_should_request_api_claims() { _resources.ApiResources.Add(new ApiResource("api1", new[] { "foo" })); var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); _mockMockProfileService.ProfileContext.RequestedClaimTypes.Should().Contain("foo"); } [Fact] public async Task GetAccessTokenClaimsAsync_should_request_api_scope_claims() { _resources.ApiResources.Add( new ApiResource("api") { Scopes = { "api1" } } ); _resources.ApiScopes.Add( new ApiScope("api1") { UserClaims = { "foo" } } ); var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); _mockMockProfileService.ProfileContext.RequestedClaimTypes.Should().Contain("foo"); } [Fact] public async Task GetAccessTokenClaimsAsync_should_request_both_api_and_api_scope_claims() { _resources.ApiResources.Add( new ApiResource("api") { UserClaims = { "foo" }, Scopes = { "api1" } } ); _resources.ApiScopes.Add( new ApiScope("api1") { UserClaims = { "bar" } } ); var claims = await _subject.GetAccessTokenClaimsAsync(_user, ResourceValidationResult, _validatedRequest); _mockMockProfileService.ProfileContext.RequestedClaimTypes.Should().Contain("foo"); _mockMockProfileService.ProfileContext.RequestedClaimTypes.Should().Contain("bar"); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DefaultConsentServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DefaultConsentServiceTests { private DefaultConsentService _subject; private MockProfileService _mockMockProfileService = new MockProfileService(); private ClaimsPrincipal _user; private Client _client; private TestUserConsentStore _userConsentStore = new TestUserConsentStore(); private StubClock _clock = new StubClock(); private DateTime now; public DefaultConsentServiceTests() { _clock.UtcNowFunc = () => UtcNow; _client = new Client { ClientId = "client", RequireConsent = true, RequirePkce = false }; _user = new IdentityServerUser("bob") { AdditionalClaims = { new Claim("foo", "foo1"), new Claim("foo", "foo2"), new Claim("bar", "bar1"), new Claim("bar", "bar2"), new Claim(JwtClaimTypes.AuthenticationContextClassReference, "acr1") } }.CreatePrincipal(); _subject = new DefaultConsentService(_clock, _userConsentStore, TestLogger.Create()); } public DateTime UtcNow { get { if (now > DateTime.MinValue) return now; return DateTime.UtcNow; } } [Fact] public async Task UpdateConsentAsync_when_client_does_not_allow_remember_consent_should_not_update_store() { _client.AllowRememberConsent = false; await _subject.UpdateConsentAsync(_user, _client, new [] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); var consent = await _userConsentStore.GetUserConsentAsync(_user.GetSubjectId(), _client.ClientId); consent.Should().BeNull(); } [Fact] public async Task UpdateConsentAsync_should_persist_consent() { await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); var consent = await _userConsentStore.GetUserConsentAsync(_user.GetSubjectId(), _client.ClientId); consent.Scopes.Count().Should().Be(2); consent.Scopes.Should().Contain("scope1"); consent.Scopes.Should().Contain("scope2"); } [Fact] public async Task UpdateConsentAsync_empty_scopes_should_should_remove_consent() { await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); await _subject.UpdateConsentAsync(_user, _client, new ParsedScopeValue[] { }); var consent = await _userConsentStore.GetUserConsentAsync(_user.GetSubjectId(), _client.ClientId); consent.Should().BeNull(); } [Fact] public async Task RequiresConsentAsync_client_does_not_require_consent_should_not_require_consent() { _client.RequireConsent = false; var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); result.Should().BeFalse(); } [Fact] public async Task RequiresConsentAsync_client_does_not_allow_remember_consent_should_require_consent() { _client.AllowRememberConsent = false; var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); result.Should().BeTrue(); } [Fact] public async Task RequiresConsentAsync_no_scopes_should_not_require_consent() { var result = await _subject.RequiresConsentAsync(_user, _client, new ParsedScopeValue[] { }); result.Should().BeFalse(); } [Fact] public async Task RequiresConsentAsync_offline_access_should_require_consent() { var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("offline_access") }); result.Should().BeTrue(); } [Fact] public async Task RequiresConsentAsync_no_prior_consent_should_require_consent() { var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); result.Should().BeTrue(); } [Fact] public async Task RequiresConsentAsync_prior_consent_should_not_require_consent() { await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); result.Should().BeFalse(); } [Fact] public async Task RequiresConsentAsync_prior_consent_with_more_scopes_should_not_require_consent() { await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2"), new ParsedScopeValue("scope3") }); var result = await _subject.RequiresConsentAsync(_user, _client, new [] { new ParsedScopeValue("scope2") }); result.Should().BeFalse(); } [Fact] public async Task RequiresConsentAsync_prior_consent_with_too_few_scopes_should_require_consent() { await _subject.UpdateConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope2"), new ParsedScopeValue("scope3") }); var result = await _subject.RequiresConsentAsync(_user, _client, new[] { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2") }); result.Should().BeTrue(); } [Fact] public async Task RequiresConsentAsync_expired_consent_should_require_consent() { now = DateTime.UtcNow; var scopes = new[] { new ParsedScopeValue("foo"), new ParsedScopeValue("bar") }; _client.ConsentLifetime = 2; await _subject.UpdateConsentAsync(_user, _client, scopes); now = now.AddSeconds(3); var result = await _subject.RequiresConsentAsync(_user, _client, scopes); result.Should().BeTrue(); } [Fact] public async Task RequiresConsentAsync_expired_consent_should_remove_consent() { now = DateTime.UtcNow; var scopes = new[] { new ParsedScopeValue("foo"), new ParsedScopeValue("bar") }; _client.ConsentLifetime = 2; await _subject.UpdateConsentAsync(_user, _client, scopes); now = now.AddSeconds(3); await _subject.RequiresConsentAsync(_user, _client, scopes); var result = await _userConsentStore.GetUserConsentAsync(_user.GetSubjectId(), _client.ClientId); result.Should().BeNull(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DefaultCorsPolicyServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Services; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DefaultCorsPolicyServiceTests { private const string Category = "DefaultCorsPolicyService"; private DefaultCorsPolicyService subject; public DefaultCorsPolicyServiceTests() { subject = new DefaultCorsPolicyService(TestLogger.Create()); } [Fact] [Trait("Category", Category)] public async Task IsOriginAllowed_null_param_ReturnsFalse() { (await subject.IsOriginAllowedAsync(null)).Should().Be(false); (await subject.IsOriginAllowedAsync(String.Empty)).Should().Be(false); (await subject.IsOriginAllowedAsync(" ")).Should().Be(false); } [Fact] [Trait("Category", Category)] public async Task IsOriginAllowed_OriginIsAllowed_ReturnsTrue() { subject.AllowedOrigins.Add("http://foo"); (await subject.IsOriginAllowedAsync("http://foo")).Should().Be(true); } [Fact] [Trait("Category", Category)] public async Task IsOriginAllowed_OriginIsNotAllowed_ReturnsFalse() { subject.AllowedOrigins.Add("http://foo"); (await subject.IsOriginAllowedAsync("http://bar")).Should().Be(false); } [Fact] [Trait("Category", Category)] public async Task IsOriginAllowed_OriginIsInAllowedList_ReturnsTrue() { subject.AllowedOrigins.Add("http://foo"); subject.AllowedOrigins.Add("http://bar"); subject.AllowedOrigins.Add("http://baz"); (await subject.IsOriginAllowedAsync("http://bar")).Should().Be(true); } [Fact] [Trait("Category", Category)] public async Task IsOriginAllowed_OriginIsNotInAllowedList_ReturnsFalse() { subject.AllowedOrigins.Add("http://foo"); subject.AllowedOrigins.Add("http://bar"); subject.AllowedOrigins.Add("http://baz"); (await subject.IsOriginAllowedAsync("http://quux")).Should().Be(false); } [Fact] [Trait("Category", Category)] public async Task IsOriginAllowed_AllowAllTrue_ReturnsTrue() { subject.AllowAll = true; (await subject.IsOriginAllowedAsync("http://foo")).Should().Be(true); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DefaultIdentityServerInteractionServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DefaultIdentityServerInteractionServiceTests { private DefaultIdentityServerInteractionService _subject; private IdentityServerOptions _options = new IdentityServerOptions(); private MockHttpContextAccessor _mockMockHttpContextAccessor; private MockMessageStore _mockEndSessionStore = new MockMessageStore(); private MockMessageStore _mockLogoutMessageStore = new MockMessageStore(); private MockMessageStore _mockErrorMessageStore = new MockMessageStore(); private MockConsentMessageStore _mockConsentStore = new MockConsentMessageStore(); private MockPersistedGrantService _mockPersistedGrantService = new MockPersistedGrantService(); private MockUserSession _mockUserSession = new MockUserSession(); private MockReturnUrlParser _mockReturnUrlParser = new MockReturnUrlParser(); private ResourceValidationResult _resourceValidationResult; public DefaultIdentityServerInteractionServiceTests() { _mockMockHttpContextAccessor = new MockHttpContextAccessor(_options, _mockUserSession, _mockEndSessionStore); _subject = new DefaultIdentityServerInteractionService(new StubClock(), _mockMockHttpContextAccessor, _mockLogoutMessageStore, _mockErrorMessageStore, _mockConsentStore, _mockPersistedGrantService, _mockUserSession, _mockReturnUrlParser, TestLogger.Create() ); _resourceValidationResult = new ResourceValidationResult(); _resourceValidationResult.Resources.IdentityResources.Add(new IdentityResources.OpenId()); _resourceValidationResult.ParsedScopes.Add(new ParsedScopeValue("openid")); } [Fact] public async Task GetLogoutContextAsync_valid_session_and_logout_id_should_not_provide_signout_iframe() { // for this, we're just confirming that since the session has changed, there's not use in doing the iframe and thsu SLO _mockUserSession.SessionId = null; _mockLogoutMessageStore.Messages.Add("id", new Message(new LogoutMessage() { SessionId = "session" })); var context = await _subject.GetLogoutContextAsync("id"); context.SignOutIFrameUrl.Should().BeNull(); } [Fact] public async Task GetLogoutContextAsync_valid_session_no_logout_id_should_provide_iframe() { _mockUserSession.Clients.Add("foo"); _mockUserSession.SessionId = "session"; _mockUserSession.User = new IdentityServerUser("123").CreatePrincipal(); var context = await _subject.GetLogoutContextAsync(null); context.SignOutIFrameUrl.Should().NotBeNull(); } [Fact] public async Task GetLogoutContextAsync_without_session_should_not_provide_iframe() { _mockUserSession.SessionId = null; _mockLogoutMessageStore.Messages.Add("id", new Message(new LogoutMessage())); var context = await _subject.GetLogoutContextAsync("id"); context.SignOutIFrameUrl.Should().BeNull(); } [Fact] public async Task CreateLogoutContextAsync_without_session_should_not_create_session() { var context = await _subject.CreateLogoutContextAsync(); context.Should().BeNull(); _mockLogoutMessageStore.Messages.Should().BeEmpty(); } [Fact] public async Task CreateLogoutContextAsync_with_session_should_create_session() { _mockUserSession.Clients.Add("foo"); _mockUserSession.User = new IdentityServerUser("123").CreatePrincipal(); _mockUserSession.SessionId = "session"; var context = await _subject.CreateLogoutContextAsync(); context.Should().NotBeNull(); _mockLogoutMessageStore.Messages.Should().NotBeEmpty(); } [Fact] public async Task GrantConsentAsync_should_throw_if_granted_and_no_subject() { Func act = async () => await _subject.GrantConsentAsync( new AuthorizationRequest(), new ConsentResponse() { ScopesValuesConsented = new[] { "openid" } }, null); (await act.Should().ThrowAsync()) .And.Message.Should().Contain("subject"); } [Fact] public async Task GrantConsentAsync_should_allow_deny_for_anonymous_users() { var req = new AuthorizationRequest() { Client = new Client { ClientId = "client" }, ValidatedResources = _resourceValidationResult }; await _subject.GrantConsentAsync(req, new ConsentResponse { Error = AuthorizationError.AccessDenied }, null); } [Fact] public async Task GrantConsentAsync_should_use_current_subject_and_create_message() { _mockUserSession.User = new IdentityServerUser("bob").CreatePrincipal(); var req = new AuthorizationRequest() { Client = new Client { ClientId = "client" }, ValidatedResources = _resourceValidationResult }; await _subject.GrantConsentAsync(req, new ConsentResponse(), null); _mockConsentStore.Messages.Should().NotBeEmpty(); var consentRequest = new ConsentRequest(req, "bob"); _mockConsentStore.Messages.First().Key.Should().Be(consentRequest.Id); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DefaultPersistedGrantServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Stores; using IdentityServer8.Stores.Serialization; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DefaultPersistedGrantServiceTests { private DefaultPersistedGrantService _subject; private InMemoryPersistedGrantStore _store = new InMemoryPersistedGrantStore(); private IAuthorizationCodeStore _codes; private IRefreshTokenStore _refreshTokens; private IReferenceTokenStore _referenceTokens; private IUserConsentStore _userConsent; private ClaimsPrincipal _user = new IdentityServerUser("123").CreatePrincipal(); public DefaultPersistedGrantServiceTests() { _subject = new DefaultPersistedGrantService( _store, new PersistentGrantSerializer(), TestLogger.Create()); _codes = new DefaultAuthorizationCodeStore(_store, new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); _refreshTokens = new DefaultRefreshTokenStore(_store, new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); _referenceTokens = new DefaultReferenceTokenStore(_store, new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); _userConsent = new DefaultUserConsentStore(_store, new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); } [Fact] public async Task GetAllGrantsAsync_should_return_all_grants() { await _userConsent.StoreUserConsentAsync(new Consent() { CreationTime = DateTime.UtcNow, ClientId = "client1", SubjectId = "123", Scopes = new string[] { "foo1", "foo2" } }); await _userConsent.StoreUserConsentAsync(new Consent() { CreationTime = DateTime.UtcNow, ClientId = "client2", SubjectId = "123", Scopes = new string[] { "foo3" } }); await _userConsent.StoreUserConsentAsync(new Consent() { CreationTime = DateTime.UtcNow, ClientId = "client1", SubjectId = "456", Scopes = new string[] { "foo3" } }); var handle1 = await _referenceTokens.StoreReferenceTokenAsync(new Token() { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "bar1"), new Claim("scope", "bar2") } }); var handle2 = await _referenceTokens.StoreReferenceTokenAsync(new Token() { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "bar3") } }); var handle3 = await _referenceTokens.StoreReferenceTokenAsync(new Token() { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "456"), new Claim("scope", "bar3") } }); var handle4 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "baz1"), new Claim("scope", "baz2") } }, Version = 1 }); var handle5 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "456"), new Claim("scope", "baz3") } }, Version = 1 }); var handle6 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "baz3") } }, Version = 1 }); var handle7 = await _codes.StoreAuthorizationCodeAsync(new AuthorizationCode() { ClientId = "client1", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = _user, CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "quux1", "quux2" } }); var handle8 = await _codes.StoreAuthorizationCodeAsync(new AuthorizationCode() { ClientId = "client2", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = _user, CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "quux3" } }); var handle9 = await _codes.StoreAuthorizationCodeAsync(new AuthorizationCode() { ClientId = "client1", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = new IdentityServerUser("456").CreatePrincipal(), CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "quux3" } }); var grants = await _subject.GetAllGrantsAsync("123"); grants.Count().Should().Be(2); var grant1 = grants.First(x => x.ClientId == "client1"); grant1.SubjectId.Should().Be("123"); grant1.ClientId.Should().Be("client1"); grant1.Scopes.Should().BeEquivalentTo(new string[] { "foo1", "foo2", "bar1", "bar2", "baz1", "baz2", "quux1", "quux2" }); var grant2 = grants.First(x => x.ClientId == "client2"); grant2.SubjectId.Should().Be("123"); grant2.ClientId.Should().Be("client2"); grant2.Scopes.Should().BeEquivalentTo(new string[] { "foo3", "bar3", "baz3", "quux3" }); } [Fact] public async Task RemoveAllGrantsAsync_should_remove_all_grants() { await _userConsent.StoreUserConsentAsync(new Consent() { ClientId = "client1", SubjectId = "123", Scopes = new string[] { "foo1", "foo2" } }); await _userConsent.StoreUserConsentAsync(new Consent() { ClientId = "client2", SubjectId = "123", Scopes = new string[] { "foo3" } }); await _userConsent.StoreUserConsentAsync(new Consent() { ClientId = "client1", SubjectId = "456", Scopes = new string[] { "foo3" } }); var handle1 = await _referenceTokens.StoreReferenceTokenAsync(new Token() { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Lifetime = 10, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "bar1"), new Claim("scope", "bar2") } }); var handle2 = await _referenceTokens.StoreReferenceTokenAsync(new Token() { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Lifetime = 10, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "bar3") } }); var handle3 = await _referenceTokens.StoreReferenceTokenAsync(new Token() { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Lifetime = 10, Type = "type", Claims = new List { new Claim("sub", "456"), new Claim("scope", "bar3") } }); var handle4 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "baz1"), new Claim("scope", "baz2") } }, Version = 1 }); var handle5 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "456"), new Claim("scope", "baz3") } }, Version = 1 }); var handle6 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "baz3") } }, Version = 1 }); var handle7 = await _codes.StoreAuthorizationCodeAsync(new AuthorizationCode() { ClientId = "client1", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = _user, CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "quux1", "quux2" } }); var handle8 = await _codes.StoreAuthorizationCodeAsync(new AuthorizationCode() { ClientId = "client2", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = _user, CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "quux3" } }); var handle9 = await _codes.StoreAuthorizationCodeAsync(new AuthorizationCode() { ClientId = "client1", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = new IdentityServerUser("456").CreatePrincipal(), CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "quux3" } }); await _subject.RemoveAllGrantsAsync("123", "client1"); (await _referenceTokens.GetReferenceTokenAsync(handle1)).Should().BeNull(); (await _referenceTokens.GetReferenceTokenAsync(handle2)).Should().NotBeNull(); (await _referenceTokens.GetReferenceTokenAsync(handle3)).Should().NotBeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle4)).Should().BeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle5)).Should().NotBeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle6)).Should().NotBeNull(); (await _codes.GetAuthorizationCodeAsync(handle7)).Should().BeNull(); (await _codes.GetAuthorizationCodeAsync(handle8)).Should().NotBeNull(); (await _codes.GetAuthorizationCodeAsync(handle9)).Should().NotBeNull(); } [Fact] public async Task RemoveAllGrantsAsync_should_filter_on_session_id() { { var handle1 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle2 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle3 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client3", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session3"), new Claim("scope", "baz") } }, Version = 1 }); await _subject.RemoveAllGrantsAsync("123"); (await _refreshTokens.GetRefreshTokenAsync(handle1)).Should().BeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle2)).Should().BeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle3)).Should().BeNull(); await _refreshTokens.RemoveRefreshTokenAsync(handle1); await _refreshTokens.RemoveRefreshTokenAsync(handle2); await _refreshTokens.RemoveRefreshTokenAsync(handle3); } { var handle1 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle2 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle3 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client3", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session3"), new Claim("scope", "baz") } }, Version = 1 }); await _subject.RemoveAllGrantsAsync("123", "client1"); (await _refreshTokens.GetRefreshTokenAsync(handle1)).Should().BeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle2)).Should().NotBeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle3)).Should().NotBeNull(); await _refreshTokens.RemoveRefreshTokenAsync(handle1); await _refreshTokens.RemoveRefreshTokenAsync(handle2); await _refreshTokens.RemoveRefreshTokenAsync(handle3); } { var handle1 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle2 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle3 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client3", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle4 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session2"), new Claim("scope", "baz") } }, Version = 1 }); await _subject.RemoveAllGrantsAsync("123", "client1", "session1"); (await _refreshTokens.GetRefreshTokenAsync(handle1)).Should().BeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle2)).Should().NotBeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle3)).Should().NotBeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle4)).Should().NotBeNull(); await _refreshTokens.RemoveRefreshTokenAsync(handle1); await _refreshTokens.RemoveRefreshTokenAsync(handle2); await _refreshTokens.RemoveRefreshTokenAsync(handle3); await _refreshTokens.RemoveRefreshTokenAsync(handle4); } { var handle1 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle2 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle3 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client3", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session1"), new Claim("scope", "baz") } }, Version = 1 }); var handle4 = await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("sid", "session2"), new Claim("scope", "baz") } }, Version = 1 }); await _subject.RemoveAllGrantsAsync("123", sessionId:"session1"); (await _refreshTokens.GetRefreshTokenAsync(handle1)).Should().BeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle2)).Should().BeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle3)).Should().BeNull(); (await _refreshTokens.GetRefreshTokenAsync(handle4)).Should().NotBeNull(); await _refreshTokens.RemoveRefreshTokenAsync(handle1); await _refreshTokens.RemoveRefreshTokenAsync(handle2); await _refreshTokens.RemoveRefreshTokenAsync(handle3); await _refreshTokens.RemoveRefreshTokenAsync(handle4); } } [Fact] public async Task GetAllGrantsAsync_should_aggregate_correctly() { await _userConsent.StoreUserConsentAsync(new Consent() { ClientId = "client1", SubjectId = "123", Scopes = new string[] { "foo1", "foo2" } }); var grants = await _subject.GetAllGrantsAsync("123"); grants.Count().Should().Be(1); grants.First().Scopes.Should().Contain(new string[] { "foo1", "foo2" }); var handle9 = await _codes.StoreAuthorizationCodeAsync(new AuthorizationCode() { ClientId = "client1", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = new IdentityServerUser("123").CreatePrincipal(), CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "quux3" } }); grants = await _subject.GetAllGrantsAsync("123"); grants.Count().Should().Be(1); grants.First().Scopes.Should().Contain(new string[] { "foo1", "foo2", "quux3" }); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DefaultRefreshTokenServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Stores; using IdentityServer8.Stores.Serialization; using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer.UnitTests.Validation.Setup; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DefaultRefreshTokenServiceTests { private DefaultRefreshTokenService _subject; private DefaultRefreshTokenStore _store; private ClaimsPrincipal _user = new IdentityServerUser("123").CreatePrincipal(); private StubClock _clock = new StubClock(); public DefaultRefreshTokenServiceTests() { _store = new DefaultRefreshTokenStore( new InMemoryPersistedGrantStore(), new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); _subject = new DefaultRefreshTokenService( _store, new TestProfileService(), _clock, TestLogger.Create()); } [Fact] public async Task CreateRefreshToken_token_exists_in_store() { var client = new Client(); var accessToken = new Token(); var handle = await _subject.CreateRefreshTokenAsync(_user, accessToken, client); (await _store.GetRefreshTokenAsync(handle)).Should().NotBeNull(); } [Fact] public async Task CreateRefreshToken_should_match_absolute_lifetime() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.ReUse, RefreshTokenExpiration = TokenExpiration.Absolute, AbsoluteRefreshTokenLifetime = 10 }; var handle = await _subject.CreateRefreshTokenAsync(_user, new Token(), client); var refreshToken = (await _store.GetRefreshTokenAsync(handle)); refreshToken.Should().NotBeNull(); refreshToken.Lifetime.Should().Be(client.AbsoluteRefreshTokenLifetime); } [Fact] public async Task CreateRefreshToken_should_cap_sliding_lifetime_that_exceeds_absolute_lifetime() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.ReUse, RefreshTokenExpiration = TokenExpiration.Sliding, SlidingRefreshTokenLifetime = 100, AbsoluteRefreshTokenLifetime = 10 }; var handle = await _subject.CreateRefreshTokenAsync(_user, new Token(), client); var refreshToken = (await _store.GetRefreshTokenAsync(handle)); refreshToken.Should().NotBeNull(); refreshToken.Lifetime.Should().Be(client.AbsoluteRefreshTokenLifetime); } [Fact] public async Task CreateRefreshToken_should_match_sliding_lifetime() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.ReUse, RefreshTokenExpiration = TokenExpiration.Sliding, SlidingRefreshTokenLifetime = 10 }; var handle = await _subject.CreateRefreshTokenAsync(_user, new Token(), client); var refreshToken = (await _store.GetRefreshTokenAsync(handle)); refreshToken.Should().NotBeNull(); refreshToken.Lifetime.Should().Be(client.SlidingRefreshTokenLifetime); } [Fact] public async Task UpdateRefreshToken_one_time_use_should_create_new_token() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.OneTimeOnly }; var refreshToken = new RefreshToken { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }; var handle = await _store.StoreRefreshTokenAsync(refreshToken); (await _subject.UpdateRefreshTokenAsync(handle, refreshToken, client)) .Should().NotBeNull() .And .NotBe(handle); } [Fact] public async Task UpdateRefreshToken_sliding_with_non_zero_absolute_should_update_lifetime() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.ReUse, RefreshTokenExpiration = TokenExpiration.Sliding, SlidingRefreshTokenLifetime = 10, AbsoluteRefreshTokenLifetime = 100 }; var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var handle = await _store.StoreRefreshTokenAsync(new RefreshToken { CreationTime = now.AddSeconds(-10), AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }); var refreshToken = await _store.GetRefreshTokenAsync(handle); var newHandle = await _subject.UpdateRefreshTokenAsync(handle, refreshToken, client); newHandle.Should().NotBeNull().And.Be(handle); var newRefreshToken = await _store.GetRefreshTokenAsync(newHandle); newRefreshToken.Should().NotBeNull(); newRefreshToken.Lifetime.Should().Be((int)(now - newRefreshToken.CreationTime).TotalSeconds + client.SlidingRefreshTokenLifetime); } [Fact] public async Task UpdateRefreshToken_lifetime_exceeds_absolute_should_be_absolute_lifetime() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.ReUse, RefreshTokenExpiration = TokenExpiration.Sliding, SlidingRefreshTokenLifetime = 10, AbsoluteRefreshTokenLifetime = 1000 }; var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var handle = await _store.StoreRefreshTokenAsync(new RefreshToken { CreationTime = now.AddSeconds(-1000), AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }); var refreshToken = await _store.GetRefreshTokenAsync(handle); var newHandle = await _subject.UpdateRefreshTokenAsync(handle, refreshToken, client); newHandle.Should().NotBeNull().And.Be(handle); var newRefreshToken = await _store.GetRefreshTokenAsync(newHandle); newRefreshToken.Should().NotBeNull(); newRefreshToken.Lifetime.Should().Be(client.AbsoluteRefreshTokenLifetime); } [Fact] public async Task UpdateRefreshToken_sliding_with_zero_absolute_should_update_lifetime() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.ReUse, RefreshTokenExpiration = TokenExpiration.Sliding, SlidingRefreshTokenLifetime = 10, AbsoluteRefreshTokenLifetime = 0 }; var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var handle = await _store.StoreRefreshTokenAsync(new RefreshToken { CreationTime = now.AddSeconds(-1000), AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }); var refreshToken = await _store.GetRefreshTokenAsync(handle); var newHandle = await _subject.UpdateRefreshTokenAsync(handle, refreshToken, client); newHandle.Should().NotBeNull().And.Be(handle); var newRefreshToken = await _store.GetRefreshTokenAsync(newHandle); newRefreshToken.Should().NotBeNull(); newRefreshToken.Lifetime.Should().Be((int)(now - newRefreshToken.CreationTime).TotalSeconds + client.SlidingRefreshTokenLifetime); } [Fact] public async Task UpdateRefreshToken_for_onetime_and_sliding_with_zero_absolute_should_update_lifetime() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.OneTimeOnly, RefreshTokenExpiration = TokenExpiration.Sliding, SlidingRefreshTokenLifetime = 10, AbsoluteRefreshTokenLifetime = 0 }; var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var handle = await _store.StoreRefreshTokenAsync(new RefreshToken { CreationTime = now.AddSeconds(-1000), AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }); var refreshToken = await _store.GetRefreshTokenAsync(handle); var newHandle = await _subject.UpdateRefreshTokenAsync(handle, refreshToken, client); newHandle.Should().NotBeNull().And.NotBe(handle); var newRefreshToken = await _store.GetRefreshTokenAsync(newHandle); newRefreshToken.Should().NotBeNull(); newRefreshToken.Lifetime.Should().Be((int)(now - newRefreshToken.CreationTime).TotalSeconds + client.SlidingRefreshTokenLifetime); } [Fact] public async Task UpdateRefreshToken_one_time_use_should_consume_token_and_create_new_one_with_correct_dates() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.OneTimeOnly }; var refreshToken = new RefreshToken { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }; var handle = await _store.StoreRefreshTokenAsync(refreshToken); var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var newHandle = await _subject.UpdateRefreshTokenAsync(handle, refreshToken, client); var oldToken = await _store.GetRefreshTokenAsync(handle); var newToken = await _store.GetRefreshTokenAsync(newHandle); oldToken.ConsumedTime.Should().Be(now); newToken.ConsumedTime.Should().BeNull(); } [Fact] public async Task ValidateRefreshToken_invalid_token_should_fail() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.OneTimeOnly }; var result = await _subject.ValidateRefreshTokenAsync("invalid", client); result.IsError.Should().BeTrue(); } [Fact] public async Task ValidateRefreshToken_client_without_allow_offline_access_should_fail() { var client = new Client { ClientId = "client1", RefreshTokenUsage = TokenUsage.OneTimeOnly }; var refreshToken = new RefreshToken { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }; var handle = await _store.StoreRefreshTokenAsync(refreshToken); var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var result = await _subject.ValidateRefreshTokenAsync(handle, client); result.IsError.Should().BeTrue(); } [Fact] public async Task ValidateRefreshToken_invalid_client_binding_should_fail() { var client = new Client { ClientId = "client1", AllowOfflineAccess = true, RefreshTokenUsage = TokenUsage.OneTimeOnly }; var refreshToken = new RefreshToken { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client2", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }; var handle = await _store.StoreRefreshTokenAsync(refreshToken); var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var result = await _subject.ValidateRefreshTokenAsync(handle, client); result.IsError.Should().BeTrue(); } [Fact] public async Task ValidateRefreshToken_expired_token_should_fail() { var client = new Client { ClientId = "client1", AllowOfflineAccess = true, RefreshTokenUsage = TokenUsage.OneTimeOnly }; var refreshToken = new RefreshToken { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }; var handle = await _store.StoreRefreshTokenAsync(refreshToken); var now = DateTime.UtcNow.AddSeconds(20); _clock.UtcNowFunc = () => now; var result = await _subject.ValidateRefreshTokenAsync(handle, client); result.IsError.Should().BeTrue(); } [Fact] public async Task ValidateRefreshToken_consumed_token_should_fail() { var client = new Client { ClientId = "client1", AllowOfflineAccess = true, RefreshTokenUsage = TokenUsage.OneTimeOnly }; var refreshToken = new RefreshToken { CreationTime = DateTime.UtcNow, Lifetime = 10, ConsumedTime = DateTime.UtcNow, AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }; var handle = await _store.StoreRefreshTokenAsync(refreshToken); var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var result = await _subject.ValidateRefreshTokenAsync(handle, client); result.IsError.Should().BeTrue(); } [Fact] public async Task ValidateRefreshToken_valid_token_should_succeed() { var client = new Client { ClientId = "client1", AllowOfflineAccess = true, RefreshTokenUsage = TokenUsage.OneTimeOnly }; var refreshToken = new RefreshToken { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = client.ClientId, Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Claims = new List() { new Claim("sub", "123") } } }; var handle = await _store.StoreRefreshTokenAsync(refreshToken); var now = DateTime.UtcNow; _clock.UtcNowFunc = () => now; var result = await _subject.ValidateRefreshTokenAsync(handle, client); result.IsError.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DefaultTokenServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DefaultTokenServiceTests { private DefaultTokenService _subject; MockClaimsService _mockClaimsService = new MockClaimsService(); MockReferenceTokenStore _mockReferenceTokenStore = new MockReferenceTokenStore(); MockTokenCreationService _mockTokenCreationService = new MockTokenCreationService(); DefaultHttpContext _httpContext = new DefaultHttpContext(); MockSystemClock _mockSystemClock = new MockSystemClock(); MockKeyMaterialService _mockKeyMaterialService = new MockKeyMaterialService(); IdentityServerOptions _options = new IdentityServerOptions(); public DefaultTokenServiceTests() { _options.IssuerUri = "https://test.identityserver8.io"; var svcs = new ServiceCollection(); svcs.AddSingleton(_options); _httpContext.RequestServices = svcs.BuildServiceProvider(); _subject = new DefaultTokenService( _mockClaimsService, _mockReferenceTokenStore, _mockTokenCreationService, new HttpContextAccessor { HttpContext = _httpContext }, _mockSystemClock, _mockKeyMaterialService, _options, TestLogger.Create()); } [Fact] public async Task CreateAccessTokenAsync_should_include_aud_for_each_ApiResource() { var request = new TokenCreationRequest { ValidatedResources = new ResourceValidationResult() { Resources = new Resources() { ApiResources = { new ApiResource("api1"){ Scopes = { "scope1" } }, new ApiResource("api2"){ Scopes = { "scope2" } }, new ApiResource("api3"){ Scopes = { "scope3" } }, }, }, ParsedScopes = { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2"), new ParsedScopeValue("scope3"), } }, ValidatedRequest = new ValidatedRequest() { Client = new Client { } } }; var result = await _subject.CreateAccessTokenAsync(request); result.Audiences.Count.Should().Be(3); result.Audiences.Should().BeEquivalentTo(new[] { "api1", "api2", "api3" }); } [Fact] public async Task CreateAccessTokenAsync_when_no_apiresources_should_not_include_any_aud() { var request = new TokenCreationRequest { ValidatedResources = new ResourceValidationResult() { Resources = new Resources() { ApiScopes = { new ApiScope("scope1"), new ApiScope("scope2"), new ApiScope("scope3"), }, }, ParsedScopes = { new ParsedScopeValue("scope1"), new ParsedScopeValue("scope2"), new ParsedScopeValue("scope3"), } }, ValidatedRequest = new ValidatedRequest() { Client = new Client { } } }; var result = await _subject.CreateAccessTokenAsync(request); result.Audiences.Count.Should().Be(0); } [Fact] public async Task CreateAccessTokenAsync_when_no_session_should_not_include_sid() { var request = new TokenCreationRequest { ValidatedResources = new ResourceValidationResult(), ValidatedRequest = new ValidatedRequest() { Client = new Client { }, SessionId = null } }; var result = await _subject.CreateAccessTokenAsync(request); result.Claims.SingleOrDefault(x => x.Type == JwtClaimTypes.SessionId).Should().BeNull(); } [Fact] public async Task CreateAccessTokenAsync_when_session_should_include_sid() { var request = new TokenCreationRequest { ValidatedResources = new ResourceValidationResult(), ValidatedRequest = new ValidatedRequest() { Client = new Client { }, SessionId = "123" } }; var result = await _subject.CreateAccessTokenAsync(request); result.Claims.SingleOrDefault(x => x.Type == JwtClaimTypes.SessionId).Value.Should().Be("123"); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DefaultUserSessionTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Linq; using System.Net; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Extensions; using IdentityServer8.Services; using Microsoft.AspNetCore.Authentication; using Microsoft.AspNetCore.Http; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DefaultUserSessionTests { private DefaultUserSession _subject; private MockHttpContextAccessor _mockHttpContext = new MockHttpContextAccessor(); private MockAuthenticationHandlerProvider _mockAuthenticationHandlerProvider = new MockAuthenticationHandlerProvider(); private MockAuthenticationHandler _mockAuthenticationHandler = new MockAuthenticationHandler(); private IdentityServerOptions _options = new IdentityServerOptions(); private ClaimsPrincipal _user; private AuthenticationProperties _props = new AuthenticationProperties(); public DefaultUserSessionTests() { _mockAuthenticationHandlerProvider.Handler = _mockAuthenticationHandler; _user = new IdentityServerUser("123").CreatePrincipal(); _subject = new DefaultUserSession( _mockHttpContext, _mockAuthenticationHandlerProvider, _options, new StubClock(), TestLogger.Create()); } [Fact] public async Task CreateSessionId_when_user_is_anonymous_should_generate_new_sid() { await _subject.CreateSessionIdAsync(_user, _props); _props.GetSessionId().Should().NotBeNull(); } [Fact] public async Task CreateSessionId_when_user_is_authenticated_should_not_generate_new_sid() { // this test is needed to allow same session id when cookie is slid // IOW, if UI layer passes in same properties dictionary, then we assume it's the same user _props.SetSessionId("999"); _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); await _subject.CreateSessionIdAsync(_user, _props); _props.GetSessionId().Should().NotBeNull(); _props.GetSessionId().Should().Be("999"); } [Fact] public async Task CreateSessionId_when_props_does_not_contain_key_should_generate_new_sid() { _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); _props.GetSessionId().Should().BeNull(); await _subject.CreateSessionIdAsync(_user, _props); _props.GetSessionId().Should().NotBeNull(); } [Fact] public async Task CreateSessionId_when_user_is_authenticated_but_different_sub_should_create_new_sid() { _props.SetSessionId("999"); _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); await _subject.CreateSessionIdAsync(new IdentityServerUser("alice").CreatePrincipal(), _props); _props.GetSessionId().Should().NotBeNull(); _props.GetSessionId().Should().NotBe("999"); } [Fact] public async Task CreateSessionId_should_issue_session_id_cookie() { await _subject.CreateSessionIdAsync(_user, _props); var cookieContainer = new CookieContainer(); var cookies = _mockHttpContext.HttpContext.Response.Headers.Where(x => x.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase)).Select(x => x.Value); cookieContainer.SetCookies(new Uri("http://server"), string.Join(",", cookies)); _mockHttpContext.HttpContext.Response.Headers.Clear(); var cookie = cookieContainer.GetCookies(new Uri("http://server")).Cast().Where(x => x.Name == _options.Authentication.CheckSessionCookieName).FirstOrDefault(); cookie.Value.Should().Be(_props.GetSessionId()); } [Fact] public async Task EnsureSessionIdCookieAsync_should_add_cookie() { _props.SetSessionId("999"); _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); await _subject.EnsureSessionIdCookieAsync(); var cookieContainer = new CookieContainer(); var cookies = _mockHttpContext.HttpContext.Response.Headers.Where(x => x.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase)).Select(x => x.Value); cookieContainer.SetCookies(new Uri("http://server"), string.Join(",", cookies)); _mockHttpContext.HttpContext.Response.Headers.Clear(); var cookie = cookieContainer.GetCookies(new Uri("http://server")).Cast().Where(x => x.Name == _options.Authentication.CheckSessionCookieName).FirstOrDefault(); cookie.Value.Should().Be("999"); } [Fact] public async Task EnsureSessionIdCookieAsync_should_not_add_cookie_if_no_sid() { await _subject.EnsureSessionIdCookieAsync(); var cookieContainer = new CookieContainer(); var cookies = _mockHttpContext.HttpContext.Response.Headers.Where(x => x.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase)).Select(x => x.Value); cookieContainer.SetCookies(new Uri("http://server"), string.Join(",", cookies)); _mockHttpContext.HttpContext.Response.Headers.Clear(); var cookie = cookieContainer.GetCookies(new Uri("http://server")).Cast().Where(x => x.Name == _options.Authentication.CheckSessionCookieName).FirstOrDefault(); cookie.Should().BeNull(); } [Fact] public async Task RemoveSessionIdCookie_should_remove_cookie() { _props.SetSessionId("999"); _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); await _subject.EnsureSessionIdCookieAsync(); var cookieContainer = new CookieContainer(); var cookies = _mockHttpContext.HttpContext.Response.Headers.Where(x => x.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase)).Select(x => x.Value); cookieContainer.SetCookies(new Uri("http://server"), string.Join(",", cookies)); _mockHttpContext.HttpContext.Response.Headers.Clear(); string cookie = cookieContainer.GetCookieHeader(new Uri("http://server")); _mockHttpContext.HttpContext.Request.Headers.Append("Cookie", cookie); await _subject.RemoveSessionIdCookieAsync(); cookies = _mockHttpContext.HttpContext.Response.Headers.Where(x => x.Key.Equals("Set-Cookie", StringComparison.OrdinalIgnoreCase)).Select(x => x.Value); cookieContainer.SetCookies(new Uri("http://server"), string.Join(",", cookies)); var query = cookieContainer.GetCookies(new Uri("http://server")).Cast().Where(x => x.Name == _options.Authentication.CheckSessionCookieName); query.Count().Should().Be(0); } [Fact] public async Task GetCurrentSessionIdAsync_when_user_is_authenticated_should_return_sid() { _props.SetSessionId("999"); _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); var sid = await _subject.GetSessionIdAsync(); sid.Should().Be("999"); } [Fact] public async Task GetCurrentSessionIdAsync_when_user_is_anonymous_should_return_null() { var sid = await _subject.GetSessionIdAsync(); sid.Should().BeNull(); } [Fact] public async Task adding_client_should_set_item_in_cookie_properties() { _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); _props.Items.Count.Should().Be(0); await _subject.AddClientIdAsync("client"); _props.Items.Count.Should().Be(1); } [Fact] public async Task when_authenticated_GetIdentityServerUserAsync_should_should_return_authenticated_user() { _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); var user = await _subject.GetUserAsync(); user.GetSubjectId().Should().Be("123"); } [Fact] public async Task when_anonymous_GetIdentityServerUserAsync_should_should_return_null() { var user = await _subject.GetUserAsync(); user.Should().BeNull(); } [Fact] public async Task corrupt_properties_entry_should_clear_entry() { _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); await _subject.AddClientIdAsync("client"); var item = _props.Items.First(); _props.Items[item.Key] = "junk"; var clients = await _subject.GetClientListAsync(); clients.Should().BeEmpty(); _props.Items.Count.Should().Be(0); } [Fact] public async Task adding_client_should_be_able_to_read_client() { _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); await _subject.AddClientIdAsync("client"); var clients = await _subject.GetClientListAsync(); clients.Should().Contain(new string[] { "client" }); } [Fact] public async Task adding_clients_should_be_able_to_read_clients() { _mockAuthenticationHandler.Result = AuthenticateResult.Success(new AuthenticationTicket(_user, _props, "scheme")); await _subject.AddClientIdAsync("client1"); await _subject.AddClientIdAsync("client2"); var clients = await _subject.GetClientListAsync(); clients.Should().Contain(new string[] { "client2", "client1" }); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/DistributedDeviceFlowThrottlingServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Text; using System.Threading; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Services; using Microsoft.Extensions.Caching.Distributed; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class DistributedDeviceFlowThrottlingServiceTests { private TestCache cache = new TestCache(); private readonly IdentityServerOptions options = new IdentityServerOptions {DeviceFlow = new DeviceFlowOptions {Interval = 5}}; private readonly DeviceCode deviceCode = new DeviceCode { Lifetime = 300, CreationTime = DateTime.UtcNow }; private const string CacheKey = "devicecode_"; private readonly DateTime testDate = new DateTime(2018, 06, 28, 13, 37, 42); [Fact] public async Task First_Poll() { var handle = Guid.NewGuid().ToString(); var service = new DistributedDeviceFlowThrottlingService(cache, new StubClock {UtcNowFunc = () => testDate}, options); var result = await service.ShouldSlowDown(handle, deviceCode); result.Should().BeFalse(); CheckCacheEntry(handle); } [Fact] public async Task Second_Poll_Too_Fast() { var handle = Guid.NewGuid().ToString(); var service = new DistributedDeviceFlowThrottlingService(cache, new StubClock { UtcNowFunc = () => testDate }, options); cache.Set(CacheKey + handle, Encoding.UTF8.GetBytes(testDate.AddSeconds(-1).ToString("O"))); var result = await service.ShouldSlowDown(handle, deviceCode); result.Should().BeTrue(); CheckCacheEntry(handle); } [Fact] public async Task Second_Poll_After_Interval() { var handle = Guid.NewGuid().ToString(); var service = new DistributedDeviceFlowThrottlingService(cache, new StubClock { UtcNowFunc = () => testDate }, options); cache.Set($"devicecode_{handle}", Encoding.UTF8.GetBytes(testDate.AddSeconds(-deviceCode.Lifetime - 1).ToString("O"))); var result = await service.ShouldSlowDown(handle, deviceCode); result.Should().BeFalse(); CheckCacheEntry(handle); } /// /// Addresses race condition from #3860 /// [Fact] public async Task Expired_Device_Code_Should_Not_Have_Expiry_in_Past() { var handle = Guid.NewGuid().ToString(); deviceCode.CreationTime = testDate.AddSeconds(-deviceCode.Lifetime * 2); var service = new DistributedDeviceFlowThrottlingService(cache, new StubClock { UtcNowFunc = () => testDate }, options); var result = await service.ShouldSlowDown(handle, deviceCode); result.Should().BeFalse(); cache.Items.TryGetValue(CacheKey + handle, out var values).Should().BeTrue(); values?.Item2.AbsoluteExpiration.Should().BeOnOrAfter(testDate); } private void CheckCacheEntry(string handle) { cache.Items.TryGetValue(CacheKey + handle, out var values).Should().BeTrue(); var dateTimeAsString = Encoding.UTF8.GetString(values?.Item1); var dateTime = DateTime.Parse(dateTimeAsString); dateTime.Should().Be(testDate); values?.Item2.AbsoluteExpiration.Should().BeCloseTo(testDate.AddSeconds(deviceCode.Lifetime), TimeSpan.FromMilliseconds(100)); } } internal class TestCache : IDistributedCache { public readonly Dictionary> Items = new Dictionary>(); public byte[] Get(string key) { if (Items.TryGetValue(key, out var value)) return value.Item1; return null; } public Task GetAsync(string key, CancellationToken token = new CancellationToken()) { return Task.FromResult(Get(key)); } public void Set(string key, byte[] value, DistributedCacheEntryOptions options) { Items.Remove(key); Items.Add(key, new Tuple(value, options)); } public Task SetAsync(string key, byte[] value, DistributedCacheEntryOptions options, CancellationToken token = new CancellationToken()) { Set(key, value, options); return Task.CompletedTask; } public void Refresh(string key) { throw new NotImplementedException(); } public Task RefreshAsync(string key, CancellationToken token = new CancellationToken()) { throw new NotImplementedException(); } public void Remove(string key) { throw new NotImplementedException(); } public Task RemoveAsync(string key, CancellationToken token = new CancellationToken()) { throw new NotImplementedException(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/Default/NumericUserCodeServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using FluentAssertions; using IdentityServer8.Services; using Xunit; namespace IdentityServer.UnitTests.Services.Default { public class NumericUserCodeGeneratorTests { [Fact] public async Task GenerateAsync_should_return_expected_code() { var sut = new NumericUserCodeGenerator(); var userCode = await sut.GenerateAsync(); var userCodeInt = int.Parse(userCode); userCodeInt.Should().BeGreaterOrEqualTo(100000000); userCodeInt.Should().BeLessOrEqualTo(999999999); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Services/InMemory/InMemoryCorsPolicyService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Models; using IdentityServer8.Services; using Xunit; namespace IdentityServer.UnitTests.Services.InMemory { public class InMemoryCorsPolicyServiceTests { private const string Category = "InMemoryCorsPolicyService"; private InMemoryCorsPolicyService _subject; private List _clients = new List(); public InMemoryCorsPolicyServiceTests() { _subject = new InMemoryCorsPolicyService(TestLogger.Create(), _clients); } [Fact] [Trait("Category", Category)] public async Task client_has_origin_should_allow_origin() { _clients.Add(new Client { AllowedCorsOrigins = new List { "http://foo" } }); (await _subject.IsOriginAllowedAsync("http://foo")).Should().BeTrue(); } [Theory] [InlineData("http://foo")] [InlineData("https://bar")] [InlineData("http://bar-baz")] [Trait("Category", Category)] public async Task client_does_not_has_origin_should_not_allow_origin(string clientOrigin) { _clients.Add(new Client { AllowedCorsOrigins = new List { clientOrigin } }); (await _subject.IsOriginAllowedAsync("http://bar")).Should().Be(false); } [Fact] [Trait("Category", Category)] public async Task client_has_many_origins_and_origin_is_in_list_should_allow_origin() { _clients.Add(new Client { AllowedCorsOrigins = new List { "http://foo", "http://bar", "http://baz" } }); (await _subject.IsOriginAllowedAsync("http://bar")).Should().Be(true); } [Fact] [Trait("Category", Category)] public async Task client_has_many_origins_and_origin_is_in_not_list_should_not_allow_origin() { _clients.Add(new Client { AllowedCorsOrigins = new List { "http://foo", "http://bar", "http://baz" } }); (await _subject.IsOriginAllowedAsync("http://quux")).Should().Be(false); } [Fact] [Trait("Category", Category)] public async Task many_clients_have_same_origins_should_allow_origin() { _clients.AddRange(new Client[] { new Client { AllowedCorsOrigins = new List { "http://foo" } }, new Client { AllowedCorsOrigins = new List { "http://foo" } } }); (await _subject.IsOriginAllowedAsync("http://foo")).Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task handle_invalid_cors_origin_format_exception() { _clients.AddRange(new Client[] { new Client { AllowedCorsOrigins = new List { "http://foo", "http://ba z" } }, new Client { AllowedCorsOrigins = new List { "http://foo", "http://bar" } } }); (await _subject.IsOriginAllowedAsync("http://bar")).Should().BeTrue(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Stores/Default/DefaultPersistedGrantStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Stores.Serialization; using Xunit; namespace IdentityServer.UnitTests.Stores.Default { public class DefaultPersistedGrantStoreTests { private InMemoryPersistedGrantStore _store = new InMemoryPersistedGrantStore(); private IAuthorizationCodeStore _codes; private IRefreshTokenStore _refreshTokens; private IReferenceTokenStore _referenceTokens; private IUserConsentStore _userConsent; private StubHandleGenerationService _stubHandleGenerationService = new StubHandleGenerationService(); private ClaimsPrincipal _user = new IdentityServerUser("123").CreatePrincipal(); public DefaultPersistedGrantStoreTests() { _codes = new DefaultAuthorizationCodeStore(_store, new PersistentGrantSerializer(), _stubHandleGenerationService, TestLogger.Create()); _refreshTokens = new DefaultRefreshTokenStore(_store, new PersistentGrantSerializer(), _stubHandleGenerationService, TestLogger.Create()); _referenceTokens = new DefaultReferenceTokenStore(_store, new PersistentGrantSerializer(), _stubHandleGenerationService, TestLogger.Create()); _userConsent = new DefaultUserConsentStore(_store, new PersistentGrantSerializer(), _stubHandleGenerationService, TestLogger.Create()); } [Fact] public async Task StoreAuthorizationCodeAsync_should_persist_grant() { var code1 = new AuthorizationCode() { ClientId = "test", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = _user, CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "scope1", "scope2" } }; var handle = await _codes.StoreAuthorizationCodeAsync(code1); var code2 = await _codes.GetAuthorizationCodeAsync(handle); code1.ClientId.Should().Be(code2.ClientId); code1.CreationTime.Should().Be(code2.CreationTime); code1.Lifetime.Should().Be(code2.Lifetime); code1.Subject.GetSubjectId().Should().Be(code2.Subject.GetSubjectId()); code1.CodeChallenge.Should().Be(code2.CodeChallenge); code1.RedirectUri.Should().Be(code2.RedirectUri); code1.Nonce.Should().Be(code2.Nonce); code1.RequestedScopes.Should().BeEquivalentTo(code2.RequestedScopes); } [Fact] public async Task RemoveAuthorizationCodeAsync_should_remove_grant() { var code1 = new AuthorizationCode() { ClientId = "test", CreationTime = DateTime.UtcNow, Lifetime = 10, Subject = _user, CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "scope1", "scope2" } }; var handle = await _codes.StoreAuthorizationCodeAsync(code1); await _codes.RemoveAuthorizationCodeAsync(handle); var code2 = await _codes.GetAuthorizationCodeAsync(handle); code2.Should().BeNull(); } [Fact] public async Task StoreRefreshTokenAsync_should_persist_grant() { var token1 = new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "foo") } }, Version = 1 }; var handle = await _refreshTokens.StoreRefreshTokenAsync(token1); var token2 = await _refreshTokens.GetRefreshTokenAsync(handle); token1.ClientId.Should().Be(token2.ClientId); token1.CreationTime.Should().Be(token2.CreationTime); token1.Lifetime.Should().Be(token2.Lifetime); token1.Subject.GetSubjectId().Should().Be(token2.Subject.GetSubjectId()); token1.Version.Should().Be(token2.Version); token1.AccessToken.Audiences.Count.Should().Be(1); token1.AccessToken.Audiences.First().Should().Be("aud"); token1.AccessToken.ClientId.Should().Be(token2.AccessToken.ClientId); token1.AccessToken.CreationTime.Should().Be(token2.AccessToken.CreationTime); token1.AccessToken.Type.Should().Be(token2.AccessToken.Type); } [Fact] public async Task RemoveRefreshTokenAsync_should_remove_grant() { var token1 = new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "foo") } }, Version = 1 }; var handle = await _refreshTokens.StoreRefreshTokenAsync(token1); await _refreshTokens.RemoveRefreshTokenAsync(handle); var token2 = await _refreshTokens.GetRefreshTokenAsync(handle); token2.Should().BeNull(); } [Fact] public async Task RemoveRefreshTokenAsync_by_sub_and_client_should_remove_grant() { var token1 = new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 10, AccessToken = new Token { ClientId = "client", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "foo") } }, Version = 1 }; var handle1 = await _refreshTokens.StoreRefreshTokenAsync(token1); var handle2 = await _refreshTokens.StoreRefreshTokenAsync(token1); await _refreshTokens.RemoveRefreshTokensAsync("123", "client"); var token2 = await _refreshTokens.GetRefreshTokenAsync(handle1); token2.Should().BeNull(); token2 = await _refreshTokens.GetRefreshTokenAsync(handle2); token2.Should().BeNull(); } [Fact] public async Task StoreReferenceTokenAsync_should_persist_grant() { var token1 = new Token() { ClientId = "client", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Lifetime = 10, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "foo") }, Version = 1 }; var handle = await _referenceTokens.StoreReferenceTokenAsync(token1); var token2 = await _referenceTokens.GetReferenceTokenAsync(handle); token1.ClientId.Should().Be(token2.ClientId); token1.Audiences.Count.Should().Be(1); token1.Audiences.First().Should().Be("aud"); token1.CreationTime.Should().Be(token2.CreationTime); token1.Type.Should().Be(token2.Type); token1.Lifetime.Should().Be(token2.Lifetime); token1.Version.Should().Be(token2.Version); } [Fact] public async Task RemoveReferenceTokenAsync_should_remove_grant() { var token1 = new Token() { ClientId = "client", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "foo") }, Version = 1 }; var handle = await _referenceTokens.StoreReferenceTokenAsync(token1); await _referenceTokens.RemoveReferenceTokenAsync(handle); var token2 = await _referenceTokens.GetReferenceTokenAsync(handle); token2.Should().BeNull(); } [Fact] public async Task RemoveReferenceTokenAsync_by_sub_and_client_should_remove_grant() { var token1 = new Token() { ClientId = "client", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "foo") }, Version = 1 }; var handle1 = await _referenceTokens.StoreReferenceTokenAsync(token1); var handle2 = await _referenceTokens.StoreReferenceTokenAsync(token1); await _referenceTokens.RemoveReferenceTokensAsync("123", "client"); var token2 = await _referenceTokens.GetReferenceTokenAsync(handle1); token2.Should().BeNull(); token2 = await _referenceTokens.GetReferenceTokenAsync(handle2); token2.Should().BeNull(); } [Fact] public async Task StoreUserConsentAsync_should_persist_grant() { var consent1 = new Consent() { CreationTime = DateTime.UtcNow, ClientId = "client", SubjectId = "123", Scopes = new string[] { "foo", "bar" } }; await _userConsent.StoreUserConsentAsync(consent1); var consent2 = await _userConsent.GetUserConsentAsync("123", "client"); consent2.ClientId.Should().Be(consent1.ClientId); consent2.SubjectId.Should().Be(consent1.SubjectId); consent2.Scopes.Should().BeEquivalentTo(new string[] { "bar", "foo" }); } [Fact] public async Task RemoveUserConsentAsync_should_remove_grant() { var consent1 = new Consent() { CreationTime = DateTime.UtcNow, ClientId = "client", SubjectId = "123", Scopes = new string[] { "foo", "bar" } }; await _userConsent.StoreUserConsentAsync(consent1); await _userConsent.RemoveUserConsentAsync("123", "client"); var consent2 = await _userConsent.GetUserConsentAsync("123", "client"); consent2.Should().BeNull(); } [Fact] public async Task same_key_for_different_grant_types_should_not_interfere_with_each_other() { _stubHandleGenerationService.Handle = "key"; await _referenceTokens.StoreReferenceTokenAsync(new Token() { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Lifetime = 10, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "bar1"), new Claim("scope", "bar2") } }); await _refreshTokens.StoreRefreshTokenAsync(new RefreshToken() { CreationTime = DateTime.UtcNow, Lifetime = 20, AccessToken = new Token { ClientId = "client1", Audiences = { "aud" }, CreationTime = DateTime.UtcNow, Type = "type", Claims = new List { new Claim("sub", "123"), new Claim("scope", "baz1"), new Claim("scope", "baz2") } }, Version = 1 }); await _codes.StoreAuthorizationCodeAsync(new AuthorizationCode() { ClientId = "client1", CreationTime = DateTime.UtcNow, Lifetime = 30, Subject = _user, CodeChallenge = "challenge", RedirectUri = "http://client/cb", Nonce = "nonce", RequestedScopes = new string[] { "quux1", "quux2" } }); (await _codes.GetAuthorizationCodeAsync("key")).Lifetime.Should().Be(30); (await _refreshTokens.GetRefreshTokenAsync("key")).Lifetime.Should().Be(20); (await _referenceTokens.GetReferenceTokenAsync("key")).Lifetime.Should().Be(10); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Stores/InMemoryClientStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Stores; using System; using System.Collections.Generic; using Xunit; using FluentAssertions; namespace IdentityServer.UnitTests.Stores { public class InMemoryClientStoreTests { [Fact] public void InMemoryClient_should_throw_if_contain_duplicate_client_ids() { List clients = new List { new Client { ClientId = "1"}, new Client { ClientId = "1"}, new Client { ClientId = "3"} }; Action act = () => new InMemoryClientStore(clients); act.Should().Throw(); } [Fact] public void InMemoryClient_should_not_throw_if_does_not_contain_duplicate_client_ids() { List clients = new List { new Client { ClientId = "1"}, new Client { ClientId = "2"}, new Client { ClientId = "3"} }; new InMemoryClientStore(clients); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Stores/InMemoryDeviceFlowStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Stores { public class InMemoryDeviceFlowStoreTests { private InMemoryDeviceFlowStore _store = new InMemoryDeviceFlowStore(); [Fact] public async Task StoreDeviceAuthorizationAsync_should_persist_data_by_user_code() { var deviceCode = Guid.NewGuid().ToString(); var userCode = Guid.NewGuid().ToString(); var data = new DeviceCode { ClientId = Guid.NewGuid().ToString(), CreationTime = DateTime.UtcNow, Lifetime = 300, IsAuthorized = false, IsOpenId = true, Subject = null, RequestedScopes = new[] {"scope1", "scope2"} }; await _store.StoreDeviceAuthorizationAsync(deviceCode, userCode, data); var foundData = await _store.FindByUserCodeAsync(userCode); foundData.ClientId.Should().Be(data.ClientId); foundData.CreationTime.Should().Be(data.CreationTime); foundData.Lifetime.Should().Be(data.Lifetime); foundData.IsAuthorized.Should().Be(data.IsAuthorized); foundData.IsOpenId.Should().Be(data.IsOpenId); foundData.Subject.Should().Be(data.Subject); foundData.RequestedScopes.Should().BeEquivalentTo(data.RequestedScopes); } [Fact] public async Task StoreDeviceAuthorizationAsync_should_persist_data_by_device_code() { var deviceCode = Guid.NewGuid().ToString(); var userCode = Guid.NewGuid().ToString(); var data = new DeviceCode { ClientId = Guid.NewGuid().ToString(), CreationTime = DateTime.UtcNow, Lifetime = 300, IsAuthorized = false, IsOpenId = true, Subject = null, RequestedScopes = new[] {"scope1", "scope2"} }; await _store.StoreDeviceAuthorizationAsync(deviceCode, userCode, data); var foundData = await _store.FindByDeviceCodeAsync(deviceCode); foundData.ClientId.Should().Be(data.ClientId); foundData.CreationTime.Should().Be(data.CreationTime); foundData.Lifetime.Should().Be(data.Lifetime); foundData.IsAuthorized.Should().Be(data.IsAuthorized); foundData.IsOpenId.Should().Be(data.IsOpenId); foundData.Subject.Should().Be(data.Subject); foundData.RequestedScopes.Should().BeEquivalentTo(data.RequestedScopes); } [Fact] public async Task UpdateByUserCodeAsync_should_update_data() { var deviceCode = Guid.NewGuid().ToString(); var userCode = Guid.NewGuid().ToString(); var initialData = new DeviceCode { ClientId = Guid.NewGuid().ToString(), CreationTime = DateTime.UtcNow, Lifetime = 300, IsAuthorized = false, IsOpenId = true, Subject = null, RequestedScopes = new[] {"scope1", "scope2"} }; await _store.StoreDeviceAuthorizationAsync(deviceCode, userCode, initialData); var updatedData = new DeviceCode { ClientId = Guid.NewGuid().ToString(), CreationTime = initialData.CreationTime.AddHours(2), Lifetime = initialData.Lifetime + 600, IsAuthorized = !initialData.IsAuthorized, IsOpenId = !initialData.IsOpenId, Subject = new ClaimsPrincipal(new ClaimsIdentity(new List {new Claim("sub", "123")})), RequestedScopes = new[] {"api1", "api2"} }; await _store.UpdateByUserCodeAsync(userCode, updatedData); var foundData = await _store.FindByUserCodeAsync(userCode); foundData.ClientId.Should().Be(updatedData.ClientId); foundData.CreationTime.Should().Be(updatedData.CreationTime); foundData.Lifetime.Should().Be(updatedData.Lifetime); foundData.IsAuthorized.Should().Be(updatedData.IsAuthorized); foundData.IsOpenId.Should().Be(updatedData.IsOpenId); foundData.Subject.Should().BeEquivalentTo(updatedData.Subject); foundData.RequestedScopes.Should().BeEquivalentTo(updatedData.RequestedScopes); } [Fact] public async Task RemoveByDeviceCodeAsync_should_update_data() { var deviceCode = Guid.NewGuid().ToString(); var userCode = Guid.NewGuid().ToString(); var data = new DeviceCode { ClientId = Guid.NewGuid().ToString(), CreationTime = DateTime.UtcNow, Lifetime = 300, IsAuthorized = false, IsOpenId = true, Subject = null, RequestedScopes = new[] { "scope1", "scope2" } }; await _store.StoreDeviceAuthorizationAsync(deviceCode, userCode, data); await _store.RemoveByDeviceCodeAsync(deviceCode); var foundData = await _store.FindByUserCodeAsync(userCode); foundData.Should().BeNull(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Stores/InMemoryPersistedGrantStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; using FluentAssertions; using System.Threading.Tasks; using System.Linq; namespace IdentityServer.UnitTests.Stores { public class InMemoryPersistedGrantStoreTests { InMemoryPersistedGrantStore _subject; public InMemoryPersistedGrantStoreTests() { _subject = new InMemoryPersistedGrantStore(); } [Fact] public async Task Store_should_persist_value() { { var item = await _subject.GetAsync("key1"); item.Should().BeNull(); } await _subject.StoreAsync(new PersistedGrant() { Key = "key1" }); { var item = await _subject.GetAsync("key1"); item.Should().NotBeNull(); } } [Fact] public async Task GetAll_should_filter() { await _subject.StoreAsync(new PersistedGrant() { Key = "key1", SubjectId = "sub1", ClientId = "client1", SessionId = "session1" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key2", SubjectId = "sub1", ClientId = "client2", SessionId = "session1" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key3", SubjectId = "sub1", ClientId = "client1", SessionId = "session2" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key4", SubjectId = "sub1", ClientId = "client3", SessionId = "session2" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key5", SubjectId = "sub1", ClientId = "client4", SessionId = "session3" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key6", SubjectId = "sub1", ClientId = "client4", SessionId = "session4" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key7", SubjectId = "sub2", ClientId = "client4", SessionId = "session4" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key1", "key2", "key3", "key4", "key5", "key6" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub2" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key7" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub3" })) .Select(x => x.Key).Should().BeEmpty(); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client1" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key1", "key3" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client2" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key2" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client3" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key4" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client4" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key5", "key6" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client5" })) .Select(x => x.Key).Should().BeEmpty(); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client1" })) .Select(x => x.Key).Should().BeEmpty(); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client4" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key7" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub3", ClientId = "client1" })) .Select(x => x.Key).Should().BeEmpty(); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client1", SessionId = "session1" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key1" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client1", SessionId = "session2" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key3" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client1", SessionId = "session3" })) .Select(x => x.Key).Should().BeEmpty(); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client2", SessionId = "session1" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key2" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client2", SessionId = "session2" })) .Select(x => x.Key).Should().BeEmpty(); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client4", SessionId = "session4" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key6" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client4", SessionId = "session4" })) .Select(x => x.Key).Should().BeEquivalentTo(new[] { "key7" }); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client4", SessionId = "session1" })) .Select(x => x.Key).Should().BeEmpty(); (await _subject.GetAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client4", SessionId = "session5" })) .Select(x => x.Key).Should().BeEmpty(); } [Fact] public async Task RemoveAll_should_filter() { { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1" }); (await _subject.GetAsync("key1")).Should().BeNull(); (await _subject.GetAsync("key2")).Should().BeNull(); (await _subject.GetAsync("key3")).Should().BeNull(); (await _subject.GetAsync("key4")).Should().BeNull(); (await _subject.GetAsync("key5")).Should().BeNull(); (await _subject.GetAsync("key6")).Should().BeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub2" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().BeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub3" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client1" }); (await _subject.GetAsync("key1")).Should().BeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().BeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client2" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().BeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client3" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().BeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client4" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().BeNull(); (await _subject.GetAsync("key6")).Should().BeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client5" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client1" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client4" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().BeNull(); (await _subject.GetAsync("key6")).Should().BeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub3", ClientId = "client1" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client1", SessionId = "session1" }); (await _subject.GetAsync("key1")).Should().BeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client1", SessionId = "session2" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().BeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client1", SessionId = "session3" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client2", SessionId = "session1" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().BeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client2", SessionId = "session2" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub1", ClientId = "client4", SessionId = "session4" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().BeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client4", SessionId = "session4" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().BeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client4", SessionId = "session1" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub2", ClientId = "client4", SessionId = "session5" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } { await Populate(); await _subject.RemoveAllAsync(new PersistedGrantFilter { SubjectId = "sub3", ClientId = "client1", SessionId = "session1" }); (await _subject.GetAsync("key1")).Should().NotBeNull(); (await _subject.GetAsync("key2")).Should().NotBeNull(); (await _subject.GetAsync("key3")).Should().NotBeNull(); (await _subject.GetAsync("key4")).Should().NotBeNull(); (await _subject.GetAsync("key5")).Should().NotBeNull(); (await _subject.GetAsync("key6")).Should().NotBeNull(); (await _subject.GetAsync("key7")).Should().NotBeNull(); } } private async Task Populate() { await _subject.StoreAsync(new PersistedGrant() { Key = "key1", SubjectId = "sub1", ClientId = "client1", SessionId = "session1" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key2", SubjectId = "sub1", ClientId = "client2", SessionId = "session1" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key3", SubjectId = "sub1", ClientId = "client1", SessionId = "session2" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key4", SubjectId = "sub1", ClientId = "client3", SessionId = "session2" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key5", SubjectId = "sub1", ClientId = "client4", SessionId = "session3" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key6", SubjectId = "sub1", ClientId = "client4", SessionId = "session4" }); await _subject.StoreAsync(new PersistedGrant() { Key = "key7", SubjectId = "sub2", ClientId = "client4", SessionId = "session4" }); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Stores/InMemoryResourcesStoreTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Stores; using System; using System.Collections.Generic; using Xunit; using FluentAssertions; namespace IdentityServer.UnitTests.Stores { public class InMemoryResourcesStoreTests { [Fact] public void InMemoryResourcesStore_should_throw_if_contains_duplicate_names() { List identityResources = new List { new IdentityResource { Name = "A" }, new IdentityResource { Name = "A" }, new IdentityResource { Name = "C" } }; List apiResources = new List { new ApiResource { Name = "B" }, new ApiResource { Name = "B" }, new ApiResource { Name = "C" } }; List scopes = new List { new ApiScope { Name = "B" }, new ApiScope { Name = "C" }, new ApiScope { Name = "C" }, }; Action act = () => new InMemoryResourcesStore(identityResources, null, null); act.Should().Throw(); act = () => new InMemoryResourcesStore(null, apiResources, null); act.Should().Throw(); act = () => new InMemoryResourcesStore(null, null, scopes); act.Should().Throw(); } [Fact] public void InMemoryResourcesStore_should_not_throw_if_does_not_contains_duplicate_names() { List identityResources = new List { new IdentityResource { Name = "A" }, new IdentityResource { Name = "B" }, new IdentityResource { Name = "C" } }; List apiResources = new List { new ApiResource { Name = "A" }, new ApiResource { Name = "B" }, new ApiResource { Name = "C" } }; List apiScopes = new List { new ApiScope { Name = "A" }, new ApiScope { Name = "B" }, new ApiScope { Name = "C" }, }; new InMemoryResourcesStore(identityResources, null, null); new InMemoryResourcesStore(null, apiResources, null); new InMemoryResourcesStore(null, null, apiScopes); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AccessTokenValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation { public class AccessTokenValidation { private const string Category = "Access token validation"; private IClientStore _clients = Factory.CreateClientStore(); private IdentityServerOptions _options = new IdentityServerOptions(); private StubClock _clock = new StubClock(); static AccessTokenValidation() { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); } private DateTime now; public DateTime UtcNow { get { if (now > DateTime.MinValue) return now; return DateTime.UtcNow; } } public AccessTokenValidation() { _clock.UtcNowFunc = () => UtcNow; } [Fact] [Trait("Category", Category)] public async Task Valid_Reference_Token() { var store = Factory.CreateReferenceTokenStore(); var validator = Factory.CreateTokenValidator(store); var token = TokenFactory.CreateAccessToken(new Client { ClientId = "roclient" }, "valid", 600, "read", "write"); var handle = await store.StoreReferenceTokenAsync(token); var result = await validator.ValidateAccessTokenAsync(handle); result.IsError.Should().BeFalse(); result.Claims.Count().Should().Be(8); result.Claims.First(c => c.Type == JwtClaimTypes.ClientId).Value.Should().Be("roclient"); } [Fact] [Trait("Category", Category)] public async Task Valid_Reference_Token_with_required_Scope() { var store = Factory.CreateReferenceTokenStore(); var validator = Factory.CreateTokenValidator(store); var token = TokenFactory.CreateAccessToken(new Client { ClientId = "roclient" }, "valid", 600, "read", "write"); var handle = await store.StoreReferenceTokenAsync(token); var result = await validator.ValidateAccessTokenAsync(handle, "read"); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Reference_Token_with_missing_Scope() { var store = Factory.CreateReferenceTokenStore(); var validator = Factory.CreateTokenValidator(store); var token = TokenFactory.CreateAccessToken(new Client { ClientId = "roclient" }, "valid", 600, "read", "write"); var handle = await store.StoreReferenceTokenAsync(token); var result = await validator.ValidateAccessTokenAsync(handle, "missing"); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InsufficientScope); } [Fact] [Trait("Category", Category)] public async Task Unknown_Reference_Token() { var validator = Factory.CreateTokenValidator(); var result = await validator.ValidateAccessTokenAsync("unknown"); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } [Fact] [Trait("Category", Category)] public async Task Reference_Token_Too_Long() { var validator = Factory.CreateTokenValidator(); var options = new IdentityServerOptions(); var longToken = "x".Repeat(options.InputLengthRestrictions.TokenHandle + 1); var result = await validator.ValidateAccessTokenAsync(longToken); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } [Fact] [Trait("Category", Category)] public async Task Expired_Reference_Token() { now = DateTime.UtcNow; var store = Factory.CreateReferenceTokenStore(); var validator = Factory.CreateTokenValidator(store, clock:_clock); var token = TokenFactory.CreateAccessToken(new Client { ClientId = "roclient" }, "valid", 2, "read", "write"); token.CreationTime = now; var handle = await store.StoreReferenceTokenAsync(token); now = now.AddSeconds(3); var result = await validator.ValidateAccessTokenAsync(handle); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.ExpiredToken); } [Fact] [Trait("Category", Category)] public async Task Malformed_JWT_Token() { var validator = Factory.CreateTokenValidator(); var result = await validator.ValidateAccessTokenAsync("unk.nown"); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } [Fact] [Trait("Category", Category)] public async Task Valid_JWT_Token() { var signer = Factory.CreateDefaultTokenCreator(); var jwt = await signer.CreateTokenAsync(TokenFactory.CreateAccessToken(new Client { ClientId = "roclient" }, "valid", 600, "read", "write")); var validator = Factory.CreateTokenValidator(null); var result = await validator.ValidateAccessTokenAsync(jwt); result.IsError.Should().BeFalse(); } [Theory] [InlineData(true)] [InlineData(false)] [Trait("Category", Category)] public async Task JWT_Token_with_scopes_have_expected_claims(bool flag) { var options = TestIdentityServerOptions.Create(); options.EmitScopesAsSpaceDelimitedStringInJwt = flag; var signer = Factory.CreateDefaultTokenCreator(options); var jwt = await signer.CreateTokenAsync(TokenFactory.CreateAccessToken(new Client { ClientId = "roclient" }, "valid", 600, "read", "write")); var validator = Factory.CreateTokenValidator(null); var result = await validator.ValidateAccessTokenAsync(jwt); result.IsError.Should().BeFalse(); result.Jwt.Should().NotBeNullOrEmpty(); result.Client.ClientId.Should().Be("roclient"); result.Claims.Count().Should().Be(8); var scopes = result.Claims.Where(c => c.Type == "scope").Select(c => c.Value).ToArray(); scopes.Count().Should().Be(2); scopes[0].Should().Be("read"); scopes[1].Should().Be("write"); } [Fact] [Trait("Category", Category)] public async Task JWT_Token_invalid_Issuer() { var signer = Factory.CreateDefaultTokenCreator(); var token = TokenFactory.CreateAccessToken(new Client { ClientId = "roclient" }, "valid", 600, "read", "write"); token.Issuer = "invalid"; var jwt = await signer.CreateTokenAsync(token); var validator = Factory.CreateTokenValidator(null); var result = await validator.ValidateAccessTokenAsync(jwt); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } [Fact] [Trait("Category", Category)] public async Task JWT_Token_Too_Long() { var signer = Factory.CreateDefaultTokenCreator(); var jwt = await signer.CreateTokenAsync(TokenFactory.CreateAccessTokenLong(new Client { ClientId = "roclient" }, "valid", 600, 1000, "read", "write")); var validator = Factory.CreateTokenValidator(null); var result = await validator.ValidateAccessTokenAsync(jwt); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } [Fact] [Trait("Category", Category)] public async Task Valid_AccessToken_but_Client_not_active() { var store = Factory.CreateReferenceTokenStore(); var validator = Factory.CreateTokenValidator(store); var token = TokenFactory.CreateAccessToken(new Client { ClientId = "unknown" }, "valid", 600, "read", "write"); var handle = await store.StoreReferenceTokenAsync(token); var result = await validator.ValidateAccessTokenAsync(handle); result.IsError.Should().BeTrue(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ClientValidation_Code.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Configuration; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ClientValidation_Code { private IdentityServerOptions _options = TestIdentityServerOptions.Create(); [Fact] [Trait("Category", "AuthorizeRequest Client Validation - Code")] public async Task Code_Request_Unknown_Scope() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "unknown"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } [Fact] [Trait("Category", "AuthorizeRequest Client Validation - Code")] public async Task OpenId_Code_Request_Invalid_RedirectUri() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://invalid"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", "AuthorizeRequest Client Validation - Code")] public async Task OpenId_Code_Request_Invalid_IdToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); } [Fact] [Trait("Category", "AuthorizeRequest Client Validation - Code")] public async Task OpenId_Code_Request_Invalid_IdTokenToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); } [Fact] [Trait("Category", "AuthorizeRequest Client Validation - Code")] public async Task OpenId_Code_Request_With_Unknown_Client() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "unknown"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); } [Fact] [Trait("Category", "AuthorizeRequest Client Validation - Code")] public async Task OpenId_Code_Request_With_Restricted_Scope() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient_restricted"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid profile"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ClientValidation_IdToken.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Configuration; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ClientValidation_IdToken { private IdentityServerOptions _options = TestIdentityServerOptions.Create(); [Fact] [Trait("Category", "AuthorizeRequest Client Validation - IdToken")] public async Task Mixed_IdToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ClientValidation_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Configuration; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ClientValidation_Invalid { private const string Category = "AuthorizeRequest Client Validation - Invalid"; private IdentityServerOptions _options = TestIdentityServerOptions.Create(); [Fact] [Trait("Category", Category)] public async Task Invalid_Protocol_Client() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "wsfed"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://wsfed/callback"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdToken); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ClientValidation_Token.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Configuration; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ClientValidation_Token { private const string Category = "AuthorizeRequest Client Validation - Token"; private IdentityServerOptions _options = TestIdentityServerOptions.Create(); [Fact] [Trait("Category", Category)] public async Task Mixed_Token_Request_Without_OpenId_Scope() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "resource profile"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Token); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task IdTokenToken_Request_with_no_AAVB() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient_no_aavb"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task CodeIdTokenToken_Request_with_no_AAVB() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient_no_aavb"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "nonce"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeIdTokenToken); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ClientValidation_Valid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Configuration; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ClientValidation_Valid { private const string Category = "AuthorizeRequest Client Validation - Valid"; private IdentityServerOptions _options = TestIdentityServerOptions.Create(); [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_Code_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Resource_Code_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_Code_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_Code_Request_Multiple_Scopes() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid profile resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_CodeIdToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "nonce"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeIdToken); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_CodeIdTokenToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "nonce"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeIdTokenToken); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_CodeIdToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "nonce"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeIdToken); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_CodeIdTokenToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "nonce"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeIdTokenToken); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_IdTokenToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_IdTokenToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_IdTokenToken_Request_Multiple_Scopes() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid profile resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Resource_Token_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Token); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", "AuthorizeRequest Client Validation - Valid")] public async Task Valid_OpenId_TokenIdToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, "token id_token"); // Unconventional order parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ProtocolValidation_CustomValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ProtocolValidation_CustomValidator { private const string Category = "AuthorizeRequest Protocol Validation"; private StubAuthorizeRequestValidator _stubAuthorizeRequestValidator = new StubAuthorizeRequestValidator(); private AuthorizeRequestValidator _subject; public Authorize_ProtocolValidation_CustomValidator() { _subject = Factory.CreateAuthorizeRequestValidator(customValidator: _stubAuthorizeRequestValidator); } [Fact] [Trait("Category", Category)] public async Task should_call_custom_validator() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var result = await _subject.ValidateAsync(parameters); _stubAuthorizeRequestValidator.WasCalled.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task should_return_error_info_from_custom_validator() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); _stubAuthorizeRequestValidator.Callback = ctx => { ctx.Result = new AuthorizeRequestValidationResult(ctx.Result.ValidatedRequest, "foo", "bar"); }; var result = await _subject.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be("foo"); result.ErrorDescription.Should().Be("bar"); } } public class StubAuthorizeRequestValidator : ICustomAuthorizeRequestValidator { public Action Callback; public bool WasCalled { get; set; } public Task ValidateAsync(CustomAuthorizeRequestValidationContext context) { WasCalled = true; Callback?.Invoke(context); return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ProtocolValidation_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ProtocolValidation_Invalid { private const string Category = "AuthorizeRequest Protocol Validation"; [Fact] [Trait("Category", Category)] public void Null_Parameter() { var validator = Factory.CreateAuthorizeRequestValidator(); Func act = () => validator.ValidateAsync(null); act.Should().ThrowAsync(); } [Fact] [Trait("Category", Category)] public async Task Empty_Parameters() { var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(new NameValueCollection()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } // fails because openid scope is requested, but no response type that indicates an identity token [Fact] [Trait("Category", Category)] public async Task OpenId_Token_Only_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, IdentityServerConstants.StandardScopes.OpenId); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Token); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Resource_Only_IdToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Mixed_Token_Only_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Token); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task OpenId_IdToken_Request_Nonce_Missing() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdToken); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Missing_ClientId() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/callback"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Missing_Scope() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Missing_RedirectUri() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "client"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Malformed_RedirectUri() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "client"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "malformed"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Malformed_RedirectUri_Triple_Slash() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "client"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https:///attacker.com"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Missing_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.UnsupportedResponseType); } [Fact] [Trait("Category", Category)] public async Task Unknown_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, "unknown"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.UnsupportedResponseType); } [Fact] [Trait("Category", Category)] public async Task Invalid_ResponseMode_For_IdToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdToken); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Query); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Invalid_ResponseMode_For_IdTokenToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Query); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Invalid_ResponseMode_For_CodeToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeToken); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Query); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Invalid_ResponseMode_For_CodeIdToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeIdToken); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Query); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Invalid_ResponseMode_For_CodeIdTokenToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeIdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Query); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Malformed_MaxAge() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); parameters.Add(OidcConstants.AuthorizeRequest.MaxAge, "malformed"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Negative_MaxAge() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); parameters.Add(OidcConstants.AuthorizeRequest.MaxAge, "-1"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Invalid_ResponseMode_For_TokenIdToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, "token id_token"); // Unconventional order parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Query); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Invalid_ResponseMode_For_IdTokenCodeToken_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, "id_token code token"); // Unconventional ordering parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Query); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task prompt_none_and_other_values_should_fail() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Fragment); parameters.Add(OidcConstants.AuthorizeRequest.Prompt, "none login"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ProtocolValidation_PKCE.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Configuration; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ProtocolValidation_Valid_PKCE { private const string Category = "AuthorizeRequest Protocol Validation - PKCE"; private InputLengthRestrictions lengths = new InputLengthRestrictions(); [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient")] [Trait("Category", Category)] public async Task valid_openid_code_request_with_challenge_and_plain_method_should_be_forbidden_if_plain_is_forbidden(string clientId) { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, clientId); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallenge, "x".Repeat(lengths.CodeChallengeMinLength)); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallengeMethod, OidcConstants.CodeChallengeMethods.Plain); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(true); result.ErrorDescription.Should().Be("Transform algorithm not supported"); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient")] [Trait("Category", Category)] public async Task valid_openid_code_request_with_challenge_and_sh256_method_should_be_allowed(string clientId) { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, clientId); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallenge, "x".Repeat(lengths.CodeChallengeMinLength)); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallengeMethod, OidcConstants.CodeChallengeMethods.Sha256); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(false); } [Theory] [InlineData("codeclient.pkce.plain")] [InlineData("codeclient.plain")] [Trait("Category", Category)] public async Task valid_openid_code_request_with_challenge_and_missing_method_should_be_allowed_if_plain_is_allowed(string clientId) { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, clientId); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallenge, "x".Repeat(lengths.CodeChallengeMinLength)); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(false); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient")] [Trait("Category", Category)] public async Task valid_openid_code_request_with_challenge_and_missing_method_should_be_forbidden_if_plain_is_forbidden(string clientId) { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, clientId); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallenge, "x".Repeat(lengths.CodeChallengeMinLength)); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(true); result.ErrorDescription.Should().Be("Transform algorithm not supported"); } [Fact] [Trait("Category", Category)] public async Task openid_code_request_missing_challenge_should_be_rejected() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient.pkce"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(true); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); result.ErrorDescription.Should().Be("code challenge required"); } [Fact] [Trait("Category", Category)] public async Task openid_hybrid_request_missing_challenge_should_be_rejected() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "hybridclient.pkce"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.CodeIdToken); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(true); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); result.ErrorDescription.Should().Be("code challenge required"); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient.pkce.plain")] [InlineData("codeclient")] [InlineData("codeclient.plain")] [Trait("Category", Category)] public async Task openid_code_request_with_challenge_and_invalid_method_should_be_rejected(string clientId) { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, clientId); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallenge, "x".Repeat(lengths.CodeChallengeMinLength)); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallengeMethod, "invalid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(true); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); result.ErrorDescription.Should().Be("Transform algorithm not supported"); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient.pkce.plain")] [InlineData("codeclient")] [InlineData("codeclient.plain")] [Trait("Category", Category)] public async Task openid_code_request_with_too_short_challenge_should_be_rejected(string clientId) { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, clientId); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallenge, "x".Repeat(lengths.CodeChallengeMinLength - 1)); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallengeMethod, OidcConstants.CodeChallengeMethods.Plain); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(true); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient.pkce.plain")] [InlineData("codeclient")] [InlineData("codeclient.plain")] [Trait("Category", Category)] public async Task openid_code_request_with_too_long_challenge_should_be_rejected(string clientId) { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, clientId); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallenge, "x".Repeat(lengths.CodeChallengeMaxLength + 1)); parameters.Add(OidcConstants.AuthorizeRequest.CodeChallengeMethod, OidcConstants.CodeChallengeMethods.Plain); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(true); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidRequest); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/AuthorizeRequest Validation/Authorize_ProtocolValidation_Valid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using Xunit; namespace IdentityServer.UnitTests.Validation.AuthorizeRequest_Validation { public class Authorize_ProtocolValidation_Valid { private const string Category = "AuthorizeRequest Protocol Validation - Valid"; [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_Code_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().Be(false); } [Fact] [Trait("Category", Category)] public async Task Valid_Resource_Code_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_Code_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Resource_Token_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Token); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_IdToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_IdTokenToken_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_IdToken_With_FormPost_ResponseMode_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, IdentityServerConstants.StandardScopes.OpenId); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdToken); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.FormPost); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_IdToken_Token_With_FormPost_ResponseMode_Request() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "implicitclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid resource"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "oob://implicit/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.IdTokenToken); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.FormPost); parameters.Add(OidcConstants.AuthorizeRequest.Nonce, "abc"); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_ResponseMode_For_Code_ResponseType() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Fragment); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task anonymous_user_should_produce_session_state_value() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Fragment); parameters.Add(OidcConstants.AuthorizeRequest.Prompt, OidcConstants.PromptModes.None); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.ValidatedRequest.SessionId.Should().NotBeNull(); } [Fact] [Trait("Category", Category)] public async Task multiple_prompt_values_should_be_accepted() { var parameters = new NameValueCollection(); parameters.Add(OidcConstants.AuthorizeRequest.ClientId, "codeclient"); parameters.Add(OidcConstants.AuthorizeRequest.Scope, "openid"); parameters.Add(OidcConstants.AuthorizeRequest.RedirectUri, "https://server/cb"); parameters.Add(OidcConstants.AuthorizeRequest.ResponseType, OidcConstants.ResponseTypes.Code); parameters.Add(OidcConstants.AuthorizeRequest.ResponseMode, OidcConstants.ResponseModes.Fragment); parameters.Add(OidcConstants.AuthorizeRequest.Prompt, OidcConstants.PromptModes.Consent.ToString() + " " + OidcConstants.PromptModes.Login.ToString()); var validator = Factory.CreateAuthorizeRequestValidator(); var result = await validator.ValidateAsync(parameters); result.ValidatedRequest.PromptModes.Count().Should().Be(2); result.ValidatedRequest.PromptModes.Should().Contain(OidcConstants.PromptModes.Login); result.ValidatedRequest.PromptModes.Should().Contain(OidcConstants.PromptModes.Consent); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/BearerTokenUsageValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IO; using System.Text; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Xunit; namespace IdentityServer.UnitTests.Validation { public class BearerTokenUsageValidation { private const string Category = "BearerTokenUsageValidator Tests"; [Fact] [Trait("Category", Category)] public async Task No_Header_no_Body_Get() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; var validator = new BearerTokenUsageValidator(TestLogger.Create< BearerTokenUsageValidator>()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task No_Header_no_Body_Post() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "POST"; var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Non_Bearer_Scheme_Header() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; ctx.Request.Headers.Append("Authorization", new string[] { "Foo Bar" }); var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Empty_Bearer_Scheme_Header() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; ctx.Request.Headers.Append("Authorization", new string[] { "Bearer" }); var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Whitespaces_Bearer_Scheme_Header() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; ctx.Request.Headers.Append("Authorization", new string[] { "Bearer " }); var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Bearer_Scheme_Header() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "GET"; ctx.Request.Headers.Append("Authorization", new string[] { "Bearer token" }); var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeTrue(); result.Token.Should().Be("token"); result.UsageType.Should().Be(BearerTokenUsageType.AuthorizationHeader); } [Fact] [Trait("Category", Category)] public async Task Valid_Body_Post() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "POST"; ctx.Request.ContentType = "application/x-www-form-urlencoded"; var body = "access_token=token"; ctx.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeTrue(); result.Token.Should().Be("token"); result.UsageType.Should().Be(BearerTokenUsageType.PostBody); } [Fact] [Trait("Category", Category)] public async Task Body_Post_empty_Token() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "POST"; ctx.Request.ContentType = "application/x-www-form-urlencoded"; var body = "access_token="; ctx.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Body_Post_Whitespace_Token() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "POST"; ctx.Request.ContentType = "application/x-www-form-urlencoded"; var body = "access_token= "; ctx.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Body_Post_no_Token() { var ctx = new DefaultHttpContext(); ctx.Request.Method = "POST"; ctx.Request.ContentType = "application/x-www-form-urlencoded"; var body = "foo=bar"; ctx.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); var validator = new BearerTokenUsageValidator(TestLogger.Create()); var result = await validator.ValidateAsync(ctx); result.TokenFound.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/ClientConfigurationValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Validation; using System; using System.Threading.Tasks; using IdentityServer.UnitTests.Validation.Setup; using Xunit; namespace IdentityServer.UnitTests.Validation { public class ClientConfigurationValidation { private const string Category = "Client Configuration Validation Tests"; private IClientConfigurationValidator _validator; IdentityServerOptions _options; public ClientConfigurationValidation() { _options = new IdentityServerOptions(); _validator = new DefaultClientConfigurationValidator(_options); } [Fact] [Trait("Category", Category)] public async Task Standard_clients_should_succeed() { foreach (var client in TestClients.Get()) { // deliberate invalid configuration if (client.ClientId == "implicit_and_client_creds") continue; var context = await ValidateAsync(client); if (!context.IsValid) { throw new System.Exception($"client {client.ClientId} failed configuration validation: {context.ErrorMessage}"); } } } [Fact] [Trait("Category", Category)] public async Task Invalid_access_token_lifetime_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ClientCredentials, RequireClientSecret = false, AllowedScopes = { "foo" }, AccessTokenLifetime = 0 }; await ShouldFailAsync(client, "access token lifetime is 0 or negative"); } [Fact] [Trait("Category", Category)] public async Task Invalid_identity_token_lifetime_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, RequireClientSecret = false, AllowedScopes = { "foo" }, IdentityTokenLifetime = 0 }; await ShouldFailAsync(client, "identity token lifetime is 0 or negative"); } [Fact] [Trait("Category", Category)] public async Task Invalid_absolute_refresh_token_lifetime_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, RequireClientSecret = false, AllowedScopes = { "foo" }, AbsoluteRefreshTokenLifetime = -1 }; await ShouldFailAsync(client, "absolute refresh token lifetime is negative"); } [Fact] [Trait("Category", Category)] public async Task Invalid_sliding_refresh_token_lifetime_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, RequireClientSecret = false, AllowedScopes = { "foo" }, SlidingRefreshTokenLifetime = -1 }; await ShouldFailAsync(client, "sliding refresh token lifetime is negative"); } [Fact] [Trait("Category", Category)] public async Task Missing_allowed_grant_type_should_fail() { var client = new Client { ClientId = "id", RequireClientSecret = false, AllowedScopes = { "foo" }, }; await ShouldFailAsync(client, "no allowed grant type specified"); } [Fact] [Trait("Category", Category)] public async Task Missing_client_secret_for_client_credentials_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "foo" }, }; await ShouldFailAsync(client, "Client secret is required for client_credentials, but no client secret is configured."); } [Fact] [Trait("Category", Category)] public async Task Missing_client_secret_for_implicit_and_client_credentials_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials, RedirectUris = { "https://foo" }, AllowedScopes = { "foo" }, }; await ShouldFailAsync(client, "Client secret is required for client_credentials, but no client secret is configured."); } [Fact] [Trait("Category", Category)] public async Task Missing_client_secret_for_hybrid_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Hybrid, RedirectUris = { "https://foo" }, AllowedScopes = { "foo" }, }; await ShouldFailAsync(client, "Client secret is required for hybrid, but no client secret is configured."); } [Fact] [Trait("Category", Category)] public async Task Missing_client_secret_for_code_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Code, RedirectUris = { "https://foo" }, AllowedScopes = { "foo" }, }; await ShouldFailAsync(client, "Client secret is required for authorization_code, but no client secret is configured."); } [Fact] [Trait("Category", Category)] public async Task Not_required_client_secret_for_hybrid_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Hybrid, RequireClientSecret = false, RedirectUris = { "https://foo" }, AllowedScopes = { "foo" }, }; var context = await ValidateAsync(client); context.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Missing_client_secret_for_implicit_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = { "foo" }, RedirectUris = { "https://foo" } }; var context = await ValidateAsync(client); context.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task empty_grant_types_collection_should_fail() { var client = new Client { ClientId = "id", AllowedGrantTypes = { }, AllowedScopes = { "foo" }, RedirectUris = { "https://foo" } }; var context = await ValidateAsync(client); await ShouldFailAsync(client, "no allowed grant type specified"); } [Fact] [Trait("Category", Category)] public async Task null_redirect_uris_collection_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("hash") }, AllowedScopes = { "foo" }, RedirectUris = null, }; var context = await ValidateAsync(client); context.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task null_post_logout_redirect_uris_collection_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("hash") }, AllowedScopes = { "foo" }, PostLogoutRedirectUris = null }; var context = await ValidateAsync(client); context.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task null_redirect_uris_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("hash") }, AllowedScopes = { "foo" }, RedirectUris = { null } }; var context = await ValidateAsync(client); context.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task null_post_logout_redirect_uris_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("hash") }, AllowedScopes = { "foo" }, PostLogoutRedirectUris = { null } }; var context = await ValidateAsync(client); context.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task empty_redirect_uris_collection_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("hash") }, AllowedScopes = { "foo" }, RedirectUris = { }, }; var context = await ValidateAsync(client); context.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task empty_post_logout_redirect_uris_collection_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("hash") }, AllowedScopes = { "foo" }, PostLogoutRedirectUris = { }, }; var context = await ValidateAsync(client); context.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task ValidateUriSchemesAsync_for_invalid_redirecturi_scheme_should_fail() { _options.Validation.InvalidRedirectUriPrefixes.Add("custom"); var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = { "foo" }, RedirectUris = { "http://callback", "custom://callback" } }; var result = await ValidateAsync(client); await ShouldFailAsync(client, "RedirectUri 'custom://callback' uses invalid scheme. If this scheme should be allowed, then configure it via ValidationOptions."); } [Fact] [Trait("Category", Category)] public async Task ValidateUriSchemesAsync_for_null_redirecturi_scheme_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = { "foo" }, RedirectUris = null }; var result = await ValidateAsync(client); result.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task ValidateUriSchemesAsync_for_valid_redirect_uri_scheme_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = { "foo" }, RedirectUris = { "http://callback", "custom://callback" } }; var result = await ValidateAsync(client); result.IsValid.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task ValidateUriSchemesAsync_for_invalid_post_logout_redirect_uri_scheme_should_fail() { _options.Validation.InvalidRedirectUriPrefixes.Add("custom"); var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = { "foo" }, RedirectUris = { "http://callback" }, PostLogoutRedirectUris = { "http://postcallback", "custom://postcallback" } }; var result = await ValidateAsync(client); await ShouldFailAsync(client, "PostLogoutRedirectUri 'custom://postcallback' uses invalid scheme. If this scheme should be allowed, then configure it via ValidationOptions."); } [Fact] [Trait("Category", Category)] public async Task ValidateUriSchemesAsync_for_valid_post_logout_redirect_uri_scheme_should_succeed() { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = { "foo" }, RedirectUris = { "http://callback" }, PostLogoutRedirectUris = { "http://postcallback", "custom://postcallback" } }; var result = await ValidateAsync(client); result.IsValid.Should().BeTrue(); } [Theory] [Trait("Category", Category)] [InlineData("bad")] [InlineData("urn:foo")] [InlineData("urn:foo:123")] [InlineData("http://foo/")] [InlineData("http://foo:80/path")] [InlineData("http://foo/path")] [InlineData("http://foo:123/path")] [InlineData("https://foo:443/path")] [InlineData("custom://foo/")] [InlineData("custom://foo/path")] [InlineData("custom://foo:443/")] [InlineData("custom://foo:443/path")] [InlineData("")] [InlineData(" ")] [InlineData((string)null)] public async Task ValidateAllowedCorsOriginsAsync_should_report_invalid_URL_format(string origin) { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Implicit, RedirectUris = { "http://client" }, AllowedCorsOrigins = { origin } }; var result = await ValidateAsync(client); result.IsValid.Should().BeFalse(); result.ErrorMessage.Should().Contain("invalid origin"); if (!String.IsNullOrWhiteSpace(origin)) { result.ErrorMessage.Should().Contain(origin); } else { result.ErrorMessage.Should().Contain("empty value"); } } [Theory] [Trait("Category", Category)] [InlineData("http://foo")] [InlineData("http://foo:80")] [InlineData("https://foo")] [InlineData("http://foo:123")] [InlineData("https://foo:456")] [InlineData("https://foo:443")] [InlineData("custom://foo")] [InlineData("custom://foo:443")] public async Task ValidateAllowedCorsOriginsAsync_should_allow_valid_formats(string origin) { var client = new Client { ClientId = "id", AllowedGrantTypes = GrantTypes.Implicit, RedirectUris = { "http://client" }, AllowedCorsOrigins = { origin } }; var result = await ValidateAsync(client); result.IsValid.Should().BeTrue(); } private async Task ValidateAsync(Client client) { var context = new ClientConfigurationValidationContext(client); await _validator.ValidateAsync(context); return context; } private async Task ShouldFailAsync(Client client, string expectedError) { var context = await ValidateAsync(client); context.IsValid.Should().BeFalse(); context.ErrorMessage.Should().Be(expectedError); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/DeviceAuthorizationRequestValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Validation { public class DeviceAuthorizationRequestValidation { private const string Category = "Device authorization request validation"; private readonly NameValueCollection testParameters = new NameValueCollection { { "scope", "resource" } }; private readonly Client testClient = new Client { ClientId = "device_flow", AllowedGrantTypes = GrantTypes.DeviceFlow, AllowedScopes = {"openid", "profile", "resource"}, AllowOfflineAccess = true }; [Fact] [Trait("Category", Category)] public void Null_Parameter() { var validator = Factory.CreateDeviceAuthorizationRequestValidator(); Func act = () => validator.ValidateAsync(null, null); act.Should().ThrowAsync(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Protocol_Client() { testClient.ProtocolType = IdentityServerConstants.ProtocolTypes.WsFederation; var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync(testParameters, new ClientSecretValidationResult {Client = testClient}); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task Invalid_Grant_Type() { testClient.AllowedGrantTypes = GrantTypes.Implicit; var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync(testParameters, new ClientSecretValidationResult {Client = testClient}); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task Unauthorized_Scope() { var parameters = new NameValueCollection {{"scope", "resource2"}}; var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync(parameters, new ClientSecretValidationResult {Client = testClient}); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Unknown_Scope() { var parameters = new NameValueCollection {{"scope", Guid.NewGuid().ToString()}}; var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync(parameters, new ClientSecretValidationResult {Client = testClient}); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Valid_OpenId_Request() { var parameters = new NameValueCollection {{"scope", "openid"}}; var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync(parameters, new ClientSecretValidationResult {Client = testClient}); result.IsError.Should().BeFalse(); result.ValidatedRequest.IsOpenIdRequest.Should().BeTrue(); result.ValidatedRequest.RequestedScopes.Should().Contain("openid"); result.ValidatedRequest.ValidatedResources.Resources.IdentityResources.Should().Contain(x => x.Name == "openid"); result.ValidatedRequest.ValidatedResources.Resources.ApiResources.Should().BeEmpty(); result.ValidatedRequest.ValidatedResources.Resources.OfflineAccess.Should().BeFalse(); result.ValidatedRequest.ValidatedResources.Resources.IdentityResources.Any().Should().BeTrue(); result.ValidatedRequest.ValidatedResources.Resources.ApiResources.Any().Should().BeFalse(); result.ValidatedRequest.ValidatedResources.Resources.OfflineAccess.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Resource_Request() { var parameters = new NameValueCollection { { "scope", "resource" } }; var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync(parameters, new ClientSecretValidationResult { Client = testClient }); result.IsError.Should().BeFalse(); result.ValidatedRequest.IsOpenIdRequest.Should().BeFalse(); result.ValidatedRequest.RequestedScopes.Should().Contain("resource"); result.ValidatedRequest.ValidatedResources.Resources.IdentityResources.Should().BeEmpty(); result.ValidatedRequest.ValidatedResources.Resources.ApiResources.Should().Contain(x => x.Name == "api"); result.ValidatedRequest.ValidatedResources.Resources.ApiScopes.Should().Contain(x => x.Name == "resource"); result.ValidatedRequest.ValidatedResources.Resources.OfflineAccess.Should().BeFalse(); result.ValidatedRequest.ValidatedResources.Resources.IdentityResources.Any().Should().BeFalse(); result.ValidatedRequest.ValidatedResources.Resources.ApiResources.Any().Should().BeTrue(); result.ValidatedRequest.ValidatedResources.Resources.ApiScopes.Any().Should().BeTrue(); result.ValidatedRequest.ValidatedResources.Resources.OfflineAccess.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Mixed_Request() { var parameters = new NameValueCollection { { "scope", "openid resource offline_access" } }; var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync(parameters, new ClientSecretValidationResult { Client = testClient }); result.IsError.Should().BeFalse(); result.ValidatedRequest.IsOpenIdRequest.Should().BeTrue(); result.ValidatedRequest.RequestedScopes.Should().Contain("openid"); result.ValidatedRequest.RequestedScopes.Should().Contain("resource"); result.ValidatedRequest.RequestedScopes.Should().Contain("offline_access"); result.ValidatedRequest.ValidatedResources.Resources.IdentityResources.Should().Contain(x => x.Name == "openid"); result.ValidatedRequest.ValidatedResources.Resources.ApiResources.Should().Contain(x => x.Name == "api"); result.ValidatedRequest.ValidatedResources.Resources.ApiScopes.Should().Contain(x => x.Name == "resource"); result.ValidatedRequest.ValidatedResources.Resources.OfflineAccess.Should().BeTrue(); result.ValidatedRequest.ValidatedResources.Resources.IdentityResources.Any().Should().BeTrue(); result.ValidatedRequest.ValidatedResources.Resources.ApiResources.Any().Should().BeTrue(); result.ValidatedRequest.ValidatedResources.Resources.ApiScopes.Any().Should().BeTrue(); result.ValidatedRequest.ValidatedResources.Resources.OfflineAccess.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Missing_Scopes_Expect_Client_Scopes() { var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync( new NameValueCollection(), new ClientSecretValidationResult { Client = testClient }); result.IsError.Should().BeFalse(); result.ValidatedRequest.RequestedScopes.Should().Contain(testClient.AllowedScopes); } [Fact] [Trait("Category", Category)] public async Task Missing_Scopes_And_Client_Scopes_Empty() { testClient.AllowedScopes.Clear(); var validator = Factory.CreateDeviceAuthorizationRequestValidator(); var result = await validator.ValidateAsync( new NameValueCollection(), new ClientSecretValidationResult { Client = testClient }); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.AuthorizeErrors.InvalidScope); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/DeviceCodeValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Validation { public class DeviceCodeValidation { private const string Category = "Device code validation"; private readonly IClientStore _clients = Factory.CreateClientStore(); private readonly DeviceCode deviceCode = new DeviceCode { ClientId = "device_flow", IsAuthorized = true, Subject = new IdentityServerUser("bob").CreatePrincipal(), IsOpenId = true, Lifetime = 300, CreationTime = DateTime.UtcNow, AuthorizedScopes = new[] { "openid", "profile", "resource" } }; [Fact] [Trait("Category", Category)] public async Task DeviceCode_Missing() { var client = await _clients.FindClientByIdAsync("device_flow"); var service = Factory.CreateDeviceCodeService(); var validator = Factory.CreateDeviceCodeValidator(service); var request = new ValidatedTokenRequest(); request.SetClient(client); var context = new DeviceCodeValidationContext { DeviceCode = null, Request = request }; await validator.ValidateAsync(context); context.Result.IsError.Should().BeTrue(); context.Result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task DeviceCode_From_Different_Client() { var badActor = await _clients.FindClientByIdAsync("codeclient"); var service = Factory.CreateDeviceCodeService(); var handle = await service.StoreDeviceAuthorizationAsync(Guid.NewGuid().ToString(), deviceCode); var validator = Factory.CreateDeviceCodeValidator(service); var request = new ValidatedTokenRequest(); request.SetClient(badActor); var context = new DeviceCodeValidationContext { DeviceCode = handle, Request = request }; await validator.ValidateAsync(context); context.Result.IsError.Should().BeTrue(); context.Result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Expired_DeviceCode() { deviceCode.CreationTime = DateTime.UtcNow.AddDays(-10); deviceCode.Lifetime = 300; var client = await _clients.FindClientByIdAsync("device_flow"); var service = Factory.CreateDeviceCodeService(); var handle = await service.StoreDeviceAuthorizationAsync(Guid.NewGuid().ToString(), deviceCode); var validator = Factory.CreateDeviceCodeValidator(service); var request = new ValidatedTokenRequest(); request.SetClient(client); var context = new DeviceCodeValidationContext { DeviceCode = handle, Request = request }; await validator.ValidateAsync(context); context.Result.IsError.Should().BeTrue(); context.Result.Error.Should().Be(OidcConstants.TokenErrors.ExpiredToken); } [Fact] [Trait("Category", Category)] public async Task Access_Denied() { deviceCode.AuthorizedScopes = new List(); var client = await _clients.FindClientByIdAsync("device_flow"); var service = Factory.CreateDeviceCodeService(); var handle = await service.StoreDeviceAuthorizationAsync(Guid.NewGuid().ToString(), deviceCode); var validator = Factory.CreateDeviceCodeValidator(service); var request = new ValidatedTokenRequest(); request.SetClient(client); var context = new DeviceCodeValidationContext { DeviceCode = handle, Request = request }; await validator.ValidateAsync(context); context.Result.IsError.Should().BeTrue(); context.Result.Error.Should().Be(OidcConstants.TokenErrors.AccessDenied); } [Fact] [Trait("Category", Category)] public async Task DeviceCode_Not_Yet_Authorized() { deviceCode.IsAuthorized = false; var client = await _clients.FindClientByIdAsync("device_flow"); var service = Factory.CreateDeviceCodeService(); var handle = await service.StoreDeviceAuthorizationAsync(Guid.NewGuid().ToString(), deviceCode); var validator = Factory.CreateDeviceCodeValidator(service); var request = new ValidatedTokenRequest(); request.SetClient(client); var context = new DeviceCodeValidationContext { DeviceCode = handle, Request = request }; await validator.ValidateAsync(context); context.Result.IsError.Should().BeTrue(); context.Result.Error.Should().Be(OidcConstants.TokenErrors.AuthorizationPending); } [Fact] [Trait("Category", Category)] public async Task DeviceCode_Missing_Subject() { deviceCode.Subject = null; var client = await _clients.FindClientByIdAsync("device_flow"); var service = Factory.CreateDeviceCodeService(); var handle = await service.StoreDeviceAuthorizationAsync(Guid.NewGuid().ToString(), deviceCode); var validator = Factory.CreateDeviceCodeValidator(service); var request = new ValidatedTokenRequest(); request.SetClient(client); var context = new DeviceCodeValidationContext { DeviceCode = handle, Request = request }; await validator.ValidateAsync(context); context.Result.IsError.Should().BeTrue(); context.Result.Error.Should().Be(OidcConstants.TokenErrors.AuthorizationPending); } [Fact] [Trait("Category", Category)] public async Task User_Disabled() { var client = await _clients.FindClientByIdAsync("device_flow"); var service = Factory.CreateDeviceCodeService(); var handle = await service.StoreDeviceAuthorizationAsync(Guid.NewGuid().ToString(), deviceCode); var validator = Factory.CreateDeviceCodeValidator(service, new TestProfileService(false)); var request = new ValidatedTokenRequest(); request.SetClient(client); var context = new DeviceCodeValidationContext { DeviceCode = handle, Request = request }; await validator.ValidateAsync(context); context.Result.IsError.Should().BeTrue(); context.Result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task DeviceCode_Polling_Too_Fast() { var client = await _clients.FindClientByIdAsync("device_flow"); var service = Factory.CreateDeviceCodeService(); var handle = await service.StoreDeviceAuthorizationAsync(Guid.NewGuid().ToString(), deviceCode); var validator = Factory.CreateDeviceCodeValidator(service, throttlingService: new TestDeviceFlowThrottlingService(true)); var request = new ValidatedTokenRequest(); request.SetClient(client); var context = new DeviceCodeValidationContext { DeviceCode = handle, Request = request }; await validator.ValidateAsync(context); context.Result.IsError.Should().BeTrue(); context.Result.Error.Should().Be(OidcConstants.TokenErrors.SlowDown); } [Fact] [Trait("Category", Category)] public async Task Valid_DeviceCode() { var client = await _clients.FindClientByIdAsync("device_flow"); var service = Factory.CreateDeviceCodeService(); var handle = await service.StoreDeviceAuthorizationAsync(Guid.NewGuid().ToString(), deviceCode); var validator = Factory.CreateDeviceCodeValidator(service); var request = new ValidatedTokenRequest(); request.SetClient(client); var context = new DeviceCodeValidationContext {DeviceCode = handle, Request = request}; await validator.ValidateAsync(context); context.Result.IsError.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/EndSessionRequestValidation/EndSessionRequestValidatorTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Validation.EndSessionRequestValidation { public class EndSessionRequestValidatorTests { private EndSessionRequestValidator _subject; private IdentityServerOptions _options; private StubTokenValidator _stubTokenValidator = new StubTokenValidator(); private StubRedirectUriValidator _stubRedirectUriValidator = new StubRedirectUriValidator(); private MockHttpContextAccessor _context = new MockHttpContextAccessor(); private MockUserSession _userSession = new MockUserSession(); private MockLogoutNotificationService _mockLogoutNotificationService = new MockLogoutNotificationService(); private MockMessageStore _mockEndSessionMessageStore = new MockMessageStore(); private ClaimsPrincipal _user; public EndSessionRequestValidatorTests() { _user = new IdentityServerUser("alice").CreatePrincipal(); _options = TestIdentityServerOptions.Create(); _subject = new EndSessionRequestValidator( _context, _options, _stubTokenValidator, _stubRedirectUriValidator, _userSession, _mockLogoutNotificationService, _mockEndSessionMessageStore, TestLogger.Create()); } [Fact] public async Task anonymous_user_when_options_require_authenticated_user_should_return_error() { _options.Authentication.RequireAuthenticatedUserForSignOutMessage = true; var parameters = new NameValueCollection(); var result = await _subject.ValidateAsync(parameters, null); result.IsError.Should().BeTrue(); result = await _subject.ValidateAsync(parameters, new ClaimsPrincipal()); result.IsError.Should().BeTrue(); result = await _subject.ValidateAsync(parameters, new ClaimsPrincipal(new ClaimsIdentity())); result.IsError.Should().BeTrue(); } [Fact] public async Task valid_params_should_return_success() { _stubTokenValidator.IdentityTokenValidationResult = new TokenValidationResult() { IsError = false, Claims = new Claim[] { new Claim("sub", _user.GetSubjectId()) }, Client = new Client() { ClientId = "client"} }; _stubRedirectUriValidator.IsPostLogoutRedirectUriValid = true; var parameters = new NameValueCollection(); parameters.Add("id_token_hint", "id_token"); parameters.Add("post_logout_redirect_uri", "http://client/signout-cb"); parameters.Add("client_id", "client1"); parameters.Add("state", "foo"); var result = await _subject.ValidateAsync(parameters, _user); result.IsError.Should().BeFalse(); result.ValidatedRequest.Client.ClientId.Should().Be("client"); result.ValidatedRequest.PostLogOutUri.Should().Be("http://client/signout-cb"); result.ValidatedRequest.State.Should().Be("foo"); result.ValidatedRequest.Subject.GetSubjectId().Should().Be(_user.GetSubjectId()); } [Fact] public async Task no_post_logout_redirect_uri_should_not_use_single_registered_uri() { _stubTokenValidator.IdentityTokenValidationResult = new TokenValidationResult() { IsError = false, Claims = new Claim[] { new Claim("sub", _user.GetSubjectId()) }, Client = new Client() { ClientId = "client1", PostLogoutRedirectUris = new List { "foo" } } }; _stubRedirectUriValidator.IsPostLogoutRedirectUriValid = true; var parameters = new NameValueCollection(); parameters.Add("id_token_hint", "id_token"); var result = await _subject.ValidateAsync(parameters, _user); result.IsError.Should().BeFalse(); result.ValidatedRequest.PostLogOutUri.Should().BeNull(); } [Fact] public async Task no_post_logout_redirect_uri_should_not_use_multiple_registered_uri() { _stubTokenValidator.IdentityTokenValidationResult = new TokenValidationResult() { IsError = false, Claims = new Claim[] { new Claim("sub", _user.GetSubjectId()) }, Client = new Client() { ClientId = "client1", PostLogoutRedirectUris = new List { "foo", "bar" } } }; _stubRedirectUriValidator.IsPostLogoutRedirectUriValid = true; var parameters = new NameValueCollection(); parameters.Add("id_token_hint", "id_token"); var result = await _subject.ValidateAsync(parameters, _user); result.IsError.Should().BeFalse(); result.ValidatedRequest.PostLogOutUri.Should().BeNull(); } [Fact] public async Task post_logout_uri_fails_validation_should_not_honor_logout_uri() { _stubTokenValidator.IdentityTokenValidationResult = new TokenValidationResult() { IsError = false, Claims = new Claim[] { new Claim("sub", _user.GetSubjectId()) }, Client = new Client() { ClientId = "client" } }; _stubRedirectUriValidator.IsPostLogoutRedirectUriValid = false; var parameters = new NameValueCollection(); parameters.Add("id_token_hint", "id_token"); parameters.Add("post_logout_redirect_uri", "http://client/signout-cb"); parameters.Add("client_id", "client1"); parameters.Add("state", "foo"); var result = await _subject.ValidateAsync(parameters, _user); result.IsError.Should().BeFalse(); result.ValidatedRequest.Client.ClientId.Should().Be("client"); result.ValidatedRequest.Subject.GetSubjectId().Should().Be(_user.GetSubjectId()); result.ValidatedRequest.State.Should().BeNull(); result.ValidatedRequest.PostLogOutUri.Should().BeNull(); } [Fact] public async Task subject_mismatch_should_return_error() { _stubTokenValidator.IdentityTokenValidationResult = new TokenValidationResult() { IsError = false, Claims = new Claim[] { new Claim("sub", "xoxo") }, Client = new Client() { ClientId = "client" } }; _stubRedirectUriValidator.IsPostLogoutRedirectUriValid = true; var parameters = new NameValueCollection(); parameters.Add("id_token_hint", "id_token"); parameters.Add("post_logout_redirect_uri", "http://client/signout-cb"); parameters.Add("client_id", "client1"); parameters.Add("state", "foo"); var result = await _subject.ValidateAsync(parameters, _user); result.IsError.Should().BeTrue(); } [Fact] public async Task successful_request_should_return_inputs() { var parameters = new NameValueCollection(); var result = await _subject.ValidateAsync(parameters, _user); result.IsError.Should().BeFalse(); result.ValidatedRequest.Raw.Should().BeSameAs(parameters); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/EndSessionRequestValidation/StubRedirectUriValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Validation.EndSessionRequestValidation { public class StubRedirectUriValidator : IRedirectUriValidator { public bool IsRedirectUriValid { get; set; } public bool IsPostLogoutRedirectUriValid { get; set; } public Task IsPostLogoutRedirectUriValidAsync(string requestedUri, Client client) { return Task.FromResult(IsPostLogoutRedirectUriValid); } public Task IsRedirectUriValidAsync(string requestedUri, Client client) { return Task.FromResult(IsRedirectUriValid); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/EndSessionRequestValidation/StubTokenValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Validation.EndSessionRequestValidation { public class StubTokenValidator : ITokenValidator { public TokenValidationResult AccessTokenValidationResult { get; set; } = new TokenValidationResult(); public TokenValidationResult IdentityTokenValidationResult { get; set; } = new TokenValidationResult(); public Task ValidateAccessTokenAsync(string token, string expectedScope = null) { return Task.FromResult(AccessTokenValidationResult); } public Task ValidateIdentityTokenAsync(string token, string clientId = null, bool validateLifetime = true) { return Task.FromResult(IdentityTokenValidationResult); } public Task ValidateRefreshTokenAsync(string token, Client client) { throw new System.NotImplementedException(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/GrantTypesValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using FluentAssertions; using IdentityServer8.Models; using Xunit; namespace IdentityServer.UnitTests.Validation { public class GrantTypesValidation { private const string Category = "Grant Types Validation"; [Fact] [Trait("Category", Category)] public void empty_should_be_allowed() { var client = new Client(); client.AllowedGrantTypes = new List(); } [Fact] [Trait("Category", Category)] public void implicit_should_be_allowed() { var client = new Client(); client.AllowedGrantTypes = GrantTypes.Implicit; } [Fact] [Trait("Category", Category)] public void custom_should_be_allowed() { var client = new Client(); client.AllowedGrantTypes = new[] { "custom" }; } [Fact] [Trait("Category", Category)] public void custom_should_be_allowed_raw() { var client = new Client(); client.AllowedGrantTypes = new[] { "custom" }; } [Theory] [Trait("Category", Category)] [InlineData(GrantType.Implicit, GrantType.Hybrid)] [InlineData(GrantType.Implicit, GrantType.AuthorizationCode)] [InlineData(GrantType.AuthorizationCode, GrantType.Hybrid)] public void forbidden_grant_type_combinations_should_throw(string type1, string type2) { var client = new Client(); Action act = () => client.AllowedGrantTypes = new[] { type1, type2 }; act.Should().Throw(); } [Theory] [Trait("Category", Category)] [InlineData(GrantType.Implicit, GrantType.Hybrid)] [InlineData(GrantType.Implicit, GrantType.AuthorizationCode)] [InlineData(GrantType.AuthorizationCode, GrantType.Hybrid)] public void custom_and_forbidden_grant_type_combinations_should_throw(string type1, string type2) { var client = new Client(); Action act = () => client.AllowedGrantTypes = new[] { "custom1", type2, "custom2", type1 }; act.Should().Throw(); } [Fact] public void duplicate_values_should_throw() { var client = new Client(); Action act = () => client.AllowedGrantTypes = new[] { "custom1", "custom2", "custom1" }; act.Should().Throw(); } [Fact] public void null_grant_type_list_should_throw_single() { var client = new Client(); Action act = () => client.AllowedGrantTypes = null; act.Should().Throw(); } [Fact] public void grant_type_with_space_should_throw_single() { var client = new Client(); Action act = () => client.AllowedGrantTypes = new[] { "custo m2" }; act.Should().Throw(); } [Fact] public void grant_type_with_space_should_throw_multiple() { var client = new Client(); Action act = () => client.AllowedGrantTypes = new[] { "custom1", "custo m2", "custom1" }; act.Should().Throw(); } [Fact] public void adding_invalid_value_to_collection_should_throw() { var client = new Client() { AllowedGrantTypes = { "implicit" } }; Action act = () => client.AllowedGrantTypes.Add("authorization_code"); act.Should().Throw(); } [Fact] public void adding_valid_value_to_collection_should_succeed() { var client = new Client() { AllowedGrantTypes = { "implicit" } }; client.AllowedGrantTypes.Add("custom"); client.AllowedGrantTypes.Count.Should().Be(2); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/IdentityTokenValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IdentityModel.Tokens.Jwt; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using Xunit; namespace IdentityServer.UnitTests.Validation { public class IdentityTokenValidation { private const string Category = "Identity token validation"; static IdentityTokenValidation() { JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); } [Fact] [Trait("Category", Category)] public async Task Valid_IdentityToken_DefaultKeyType() { var creator = Factory.CreateDefaultTokenCreator(); var token = TokenFactory.CreateIdentityToken("roclient", "valid"); var jwt = await creator.CreateTokenAsync(token); var validator = Factory.CreateTokenValidator(); var result = await validator.ValidateIdentityTokenAsync(jwt, "roclient"); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_IdentityToken_DefaultKeyType_no_ClientId_supplied() { var creator = Factory.CreateDefaultTokenCreator(); var jwt = await creator.CreateTokenAsync(TokenFactory.CreateIdentityToken("roclient", "valid")); var validator = Factory.CreateTokenValidator(); var result = await validator.ValidateIdentityTokenAsync(jwt, "roclient"); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_IdentityToken_no_ClientId_supplied() { var creator = Factory.CreateDefaultTokenCreator(); var jwt = await creator.CreateTokenAsync(TokenFactory.CreateIdentityToken("roclient", "valid")); var validator = Factory.CreateTokenValidator(); var result = await validator.ValidateIdentityTokenAsync(jwt); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task IdentityToken_InvalidClientId() { var creator = Factory.CreateDefaultTokenCreator(); var jwt = await creator.CreateTokenAsync(TokenFactory.CreateIdentityToken("roclient", "valid")); var validator = Factory.CreateTokenValidator(); var result = await validator.ValidateIdentityTokenAsync(jwt, "invalid"); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } [Fact] [Trait("Category", Category)] public async Task IdentityToken_Too_Long() { var creator = Factory.CreateDefaultTokenCreator(); var jwt = await creator.CreateTokenAsync(TokenFactory.CreateIdentityTokenLong("roclient", "valid", 1000)); var validator = Factory.CreateTokenValidator(); var result = await validator.ValidateIdentityTokenAsync(jwt, "roclient"); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/IntrospectionRequestValidatorTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Validation { public class IntrospectionRequestValidatorTests { private const string Category = "Introspection request validation"; private IntrospectionRequestValidator _subject; private IReferenceTokenStore _referenceTokenStore; public IntrospectionRequestValidatorTests() { _referenceTokenStore = Factory.CreateReferenceTokenStore(); var tokenValidator = Factory.CreateTokenValidator(_referenceTokenStore); _subject = new IntrospectionRequestValidator(tokenValidator, TestLogger.Create()); } [Fact] [Trait("Category", Category)] public async Task valid_token_should_successfully_validate() { var token = new Token { CreationTime = DateTime.UtcNow, Issuer = "http://op", ClientId = "codeclient", Lifetime = 1000, Claims = { new System.Security.Claims.Claim("scope", "a"), new System.Security.Claims.Claim("scope", "b") } }; var handle = await _referenceTokenStore.StoreReferenceTokenAsync(token); var param = new NameValueCollection() { { "token", handle} }; var result = await _subject.ValidateAsync(param, null); result.IsError.Should().Be(false); result.IsActive.Should().Be(true); result.Claims.Count().Should().Be(5); result.Token.Should().Be(handle); } [Fact] [Trait("Category", Category)] public async Task missing_token_should_error() { var param = new NameValueCollection(); var result = await _subject.ValidateAsync(param, null); result.IsError.Should().Be(true); result.Error.Should().Be("missing_token"); result.IsActive.Should().Be(false); result.Claims.Should().BeNull(); result.Token.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task invalid_token_should_return_inactive() { var param = new NameValueCollection() { { "token", "invalid" } }; var result = await _subject.ValidateAsync(param, null); result.IsError.Should().Be(false); result.IsActive.Should().Be(false); result.Claims.Should().BeNull(); result.Token.Should().Be("invalid"); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/ResourceValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Extensions; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation { public class ResourceValidation { private const string Category = "Resource Validation"; private List _identityResources = new List { new IdentityResource { Name = "openid", Required = true }, new IdentityResource { Name = "email" } }; private List _apiResources = new List { new ApiResource { Name = "api", Scopes = { "resource1", "resource2" } }, new ApiResource { Name = "disabled_api", Enabled = false, Scopes = { "disabled" } } }; private List _scopes = new List { new ApiScope { Name = "resource1", Required = true }, new ApiScope { Name = "resource2" } }; private Client _restrictedClient = new Client { ClientId = "restricted", AllowedScopes = new List { "openid", "resource1", "disabled" } }; private IResourceStore _store; public ResourceValidation() { _store = new InMemoryResourcesStore(_identityResources, _apiResources, _scopes); } [Fact] [Trait("Category", Category)] public void Parse_Scopes_with_Empty_Scope_List() { var scopes = string.Empty.ParseScopesString(); scopes.Should().BeNull(); } [Fact] [Trait("Category", Category)] public void Parse_Scopes_with_Sorting() { var scopes = "scope3 scope2 scope1".ParseScopesString(); scopes.Count.Should().Be(3); scopes[0].Should().Be("scope1"); scopes[1].Should().Be("scope2"); scopes[2].Should().Be("scope3"); } [Fact] [Trait("Category", Category)] public void Parse_Scopes_with_Extra_Spaces() { var scopes = " scope3 scope2 scope1 ".ParseScopesString(); scopes.Count.Should().Be(3); scopes[0].Should().Be("scope1"); scopes[1].Should().Be("scope2"); scopes[2].Should().Be("scope3"); } [Fact] [Trait("Category", Category)] public void Parse_Scopes_with_Duplicate_Scope() { var scopes = "scope2 scope1 scope2".ParseScopesString(); scopes.Count.Should().Be(2); scopes[0].Should().Be("scope1"); scopes[1].Should().Be("scope2"); } [Fact] [Trait("Category", Category)] public async Task Only_Offline_Access_Requested() { var scopes = "offline_access".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeFalse(); result.InvalidScopes.Should().Contain("offline_access"); } [Fact] [Trait("Category", Category)] public async Task All_Scopes_Valid() { var scopes = "openid resource1".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeTrue(); result.InvalidScopes.Should().BeEmpty(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Scope() { { var scopes = "openid email resource1 unknown".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeFalse(); result.InvalidScopes.Should().Contain("unknown"); result.InvalidScopes.Should().Contain("email"); } { var scopes = "openid resource1 resource2".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeFalse(); result.InvalidScopes.Should().Contain("resource2"); } { var scopes = "openid email resource1".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeFalse(); result.InvalidScopes.Should().Contain("email"); } } [Fact] [Trait("Category", Category)] public async Task Disabled_Scope() { var scopes = "openid resource1 disabled".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeFalse(); result.InvalidScopes.Should().Contain("disabled"); } [Fact] [Trait("Category", Category)] public async Task All_Scopes_Allowed_For_Restricted_Client() { var scopes = "openid resource1".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeTrue(); result.InvalidScopes.Should().BeEmpty(); } [Fact] [Trait("Category", Category)] public async Task Restricted_Scopes() { var scopes = "openid email resource1 resource2".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeFalse(); result.InvalidScopes.Should().Contain("email"); result.InvalidScopes.Should().Contain("resource2"); } [Fact] [Trait("Category", Category)] public async Task Contains_Resource_and_Identity_Scopes() { var scopes = "openid resource1".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeTrue(); result.Resources.IdentityResources.SelectMany(x => x.Name).Should().Contain("openid"); result.Resources.ApiScopes.Select(x => x.Name).Should().Contain("resource1"); } [Fact] [Trait("Category", Category)] public async Task Contains_Resource_Scopes_Only() { var scopes = "resource1".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeTrue(); result.Resources.IdentityResources.Should().BeEmpty(); result.Resources.ApiScopes.Select(x => x.Name).Should().Contain("resource1"); } [Fact] [Trait("Category", Category)] public async Task Contains_Identity_Scopes_Only() { var scopes = "openid".ParseScopesString(); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = _restrictedClient, Scopes = scopes }); result.Succeeded.Should().BeTrue(); result.Resources.IdentityResources.SelectMany(x => x.Name).Should().Contain("openid"); result.Resources.ApiResources.Should().BeEmpty(); } [Fact] [Trait("Category", Category)] public async Task Scope_matches_multipls_apis_should_succeed() { _apiResources.Clear(); _apiResources.Add(new ApiResource { Name = "api1", Scopes = { "resource" } }); _apiResources.Add(new ApiResource { Name = "api2", Scopes = { "resource" } }); _scopes.Clear(); _scopes.Add(new ApiScope("resource")); var validator = Factory.CreateResourceValidator(_store); var result = await validator.ValidateRequestedResourcesAsync(new IdentityServer8.Validation.ResourceValidationRequest { Client = new Client { AllowedScopes = { "resource" } }, Scopes = new[] { "resource" } }); result.Succeeded.Should().BeTrue(); result.Resources.ApiResources.Count.Should().Be(2); result.Resources.ApiResources.Select(x => x.Name).Should().BeEquivalentTo(new[] { "api1", "api2" }); result.RawScopeValues.Count().Should().Be(1); result.RawScopeValues.Should().BeEquivalentTo(new[] { "resource" }); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/ResponseTypeEqualityComparison.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Validation { /// /// Tests for ResponseTypeEqualityComparer /// /// /// Some of these are pretty fundamental equality checks, but the purpose here is to ensure the /// important property: that the order is insignificant when multiple values are /// sent in a space-delimited string. We want to ensure that property holds and at the same time /// the basic equality function works as well. /// public class ResponseTypeEqualityComparison { /// /// These tests ensure that single-value strings compare with the /// same behavior as default string comparisons. /// public class SingleValueStringComparisons { [Fact] public void Both_null() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = null; string y = null; var result = comparer.Equals(x, y); var expected = (x == y); result.Should().Be(expected); } [Fact] public void Left_null_other_not() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = null; string y = string.Empty; var result = comparer.Equals(x, y); var expected = (x == y); result.Should().Be(expected); } [Fact] public void Right_null_other_not() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = string.Empty; string y = null; var result = comparer.Equals(x, y); var expected = (x == y); result.Should().Be(expected); } [Fact] public void token_token() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "token"; string y = "token"; var result = comparer.Equals(x, y); var expected = (x == y); result.Should().Be(expected); } [Fact] public void id_token_id_token() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "id_token"; string y = "id_token"; var result = comparer.Equals(x, y); var expected = (x == y); result.Should().Be(expected); } [Fact] public void id_token_token() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "id_token"; string y = "token"; var result = comparer.Equals(x, y); var expected = (x == y); result.Should().Be(expected); } } /// /// These tests ensure the property demanded by the /// OAuth2 spec /// where, in a space-delimited list of values, the order is not important. /// public class MultipleValueStringComparisons { [Fact] public void id_token_token_both_ways() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "id_token token"; string y = "token id_token"; var result = comparer.Equals(x, y); result.Should().BeTrue(); } [Fact] public void code_id_token_both_ways() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "code id_token"; string y = "id_token code"; var result = comparer.Equals(x, y); result.Should().BeTrue(); } [Fact] public void code_token_both_ways() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "code token"; string y = "token code"; var result = comparer.Equals(x, y); result.Should().BeTrue(); } [Fact] public void code_id_token_token_combo1() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "code id_token token"; string y = "id_token code token"; var result = comparer.Equals(x, y); result.Should().BeTrue(); } [Fact] public void code_id_token_token_combo2() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "code id_token token"; string y = "token id_token code"; var result = comparer.Equals(x, y); result.Should().BeTrue(); } [Fact] public void code_id_token_token_missing_code() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "code id_token token"; string y = "id_token token"; var result = comparer.Equals(x, y); result.Should().BeFalse(); } [Fact] public void code_id_token_token_missing_code_and_token() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "code id_token token"; string y = "id_token"; var result = comparer.Equals(x, y); result.Should().BeFalse(); } [Fact] public void Totally_different_words() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "blerg smoo"; string y = "token code"; var result = comparer.Equals(x, y); result.Should().BeFalse(); } [Fact] public void Same_length_different_count() { ResponseTypeEqualityComparer comparer = new ResponseTypeEqualityComparer(); string x = "code id_token token"; string y = "tokenizer bleegerfi"; var result = comparer.Equals(x, y); result.Should().BeFalse(); } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/RevocationRequestValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Validation { public class RevocationRequestValidation { private const string Category = "Revocation Request Validation Tests"; private ITokenRevocationRequestValidator _validator; private Client _client; public RevocationRequestValidation() { _validator = new TokenRevocationRequestValidator(TestLogger.Create()); _client = new Client { ClientName = "Code Client", Enabled = true, ClientId = "codeclient", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequireConsent = false, RedirectUris = new List { "https://server/cb" }, AuthorizationCodeLifetime = 60 }; } [Fact] [Trait("Category", Category)] public async Task Empty_Parameters() { var parameters = new NameValueCollection(); var result = await _validator.ValidateRequestAsync(parameters, _client); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Missing_Token_Valid_Hint() { var parameters = new NameValueCollection { { "token_type_hint", "access_token" } }; var result = await _validator.ValidateRequestAsync(parameters, _client); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task Valid_Token_And_AccessTokenHint() { var parameters = new NameValueCollection { { "token", "foo" }, { "token_type_hint", "access_token" } }; var result = await _validator.ValidateRequestAsync(parameters, _client); result.IsError.Should().BeFalse(); result.Token.Should().Be("foo"); result.TokenTypeHint.Should().Be("access_token"); } [Fact] [Trait("Category", Category)] public async Task Valid_Token_and_RefreshTokenHint() { var parameters = new NameValueCollection { { "token", "foo" }, { "token_type_hint", "refresh_token" } }; var result = await _validator.ValidateRequestAsync(parameters, _client); result.IsError.Should().BeFalse(); result.Token.Should().Be("foo"); result.TokenTypeHint.Should().Be("refresh_token"); } [Fact] [Trait("Category", Category)] public async Task Valid_Token_And_Missing_Hint() { var parameters = new NameValueCollection { { "token", "foo" } }; var result = await _validator.ValidateRequestAsync(parameters, _client); result.IsError.Should().BeFalse(); result.Token.Should().Be("foo"); result.TokenTypeHint.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task Valid_Token_And_Invalid_Hint() { var parameters = new NameValueCollection { { "token", "foo" }, { "token_type_hint", "invalid" } }; var result = await _validator.ValidateRequestAsync(parameters, _client); result.IsError.Should().BeTrue(); result.Error.Should().Be(Constants.RevocationErrors.UnsupportedTokenType); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/BasicAuthenticationCredentialParsing.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Text; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Primitives; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class BasicAuthenticationSecretParsing { private const string Category = "Secrets - Basic Authentication Secret Parsing"; private IdentityServerOptions _options; private BasicAuthenticationSecretParser _parser; public BasicAuthenticationSecretParsing() { _options = new IdentityServerOptions(); _parser = new BasicAuthenticationSecretParser(_options, TestLogger.Create()); } [Fact] [Trait("Category", Category)] public async void EmptyContext() { var context = new DefaultHttpContext(); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void Valid_BasicAuthentication_Request() { var context = new DefaultHttpContext(); var headerValue = string.Format("Basic {0}", Convert.ToBase64String(Encoding.UTF8.GetBytes("client:secret"))); context.Request.Headers.Append("Authorization", new StringValues(headerValue)); var secret = await _parser.ParseAsync(context); secret.Type.Should().Be(IdentityServerConstants.ParsedSecretTypes.SharedSecret); secret.Id.Should().Be("client"); secret.Credential.Should().Be("secret"); } [Fact] [Trait("Category", Category)] public async void Valid_BasicAuthentication_Request_With_UserName_Only_And_Colon_For_Optional_ClientSecret() { var context = new DefaultHttpContext(); var headerValue = string.Format("Basic {0}", Convert.ToBase64String(Encoding.UTF8.GetBytes("client:"))); context.Request.Headers.Append("Authorization", new StringValues(headerValue)); var secret = await _parser.ParseAsync(context); secret.Type.Should().Be(IdentityServerConstants.ParsedSecretTypes.NoSecret); secret.Id.Should().Be("client"); secret.Credential.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void BasicAuthentication_Request_With_Empty_Basic_Header() { var context = new DefaultHttpContext(); context.Request.Headers.Append("Authorization", new StringValues(string.Empty)); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void Valid_BasicAuthentication_Request_ClientId_Too_Long() { var context = new DefaultHttpContext(); var longClientId = "x".Repeat(_options.InputLengthRestrictions.ClientId + 1); var credential = string.Format("{0}:secret", longClientId); var headerValue = string.Format("Basic {0}", Convert.ToBase64String(Encoding.UTF8.GetBytes(credential))); context.Request.Headers.Append("Authorization", new StringValues(headerValue)); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void Valid_BasicAuthentication_Request_ClientSecret_Too_Long() { var context = new DefaultHttpContext(); var longClientSecret = "x".Repeat(_options.InputLengthRestrictions.ClientSecret + 1); var credential = string.Format("client:{0}", longClientSecret); var headerValue = string.Format("Basic {0}", Convert.ToBase64String(Encoding.UTF8.GetBytes(credential))); context.Request.Headers.Append("Authorization", new StringValues(headerValue)); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void BasicAuthentication_Request_With_Empty_Basic_Header_Variation() { var context = new DefaultHttpContext(); context.Request.Headers.Append("Authorization", new StringValues("Basic ")); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void BasicAuthentication_Request_With_Unknown_Scheme() { var context = new DefaultHttpContext(); context.Request.Headers.Append("Authorization", new StringValues("Unknown")); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void BasicAuthentication_Request_With_Malformed_Credentials_NoBase64_Encoding() { var context = new DefaultHttpContext(); context.Request.Headers.Append("Authorization", new StringValues("Basic somerandomdata")); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void BasicAuthentication_Request_With_Malformed_Credentials_Base64_Encoding_UserName_Only() { var context = new DefaultHttpContext(); var headerValue = string.Format("Basic {0}", Convert.ToBase64String(Encoding.UTF8.GetBytes("client"))); context.Request.Headers.Append("Authorization", new StringValues(headerValue)); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/ClientAssertionSecretParsing.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IdentityModel.Tokens.Jwt; using System.IO; using System.Security.Claims; using System.Text; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class ClientAssertionSecretParsing { private IdentityServerOptions _options; private JwtBearerClientAssertionSecretParser _parser; public ClientAssertionSecretParsing() { _options = new IdentityServerOptions(); _parser = new JwtBearerClientAssertionSecretParser(_options, new LoggerFactory().CreateLogger()); } [Fact] public async void EmptyContext() { var context = new DefaultHttpContext(); context.Request.Body = new MemoryStream(); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] public async void Valid_ClientAssertion() { var context = new DefaultHttpContext(); var token = new JwtSecurityToken(issuer: "issuer", claims: new[] { new Claim("sub", "client") }); var tokenString = new JwtSecurityTokenHandler().WriteToken(token); var body = "client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=" + tokenString; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().NotBeNull(); secret.Type.Should().Be(IdentityServerConstants.ParsedSecretTypes.JwtBearer); secret.Id.Should().Be("client"); secret.Credential.Should().Be(tokenString); } [Fact] public async void Missing_ClientAssertionType() { var context = new DefaultHttpContext(); var body = "client_id=client&client_assertion=token"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] public async void Missing_ClientAssertion() { var context = new DefaultHttpContext(); var body = "client_id=client&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] public async void Malformed_PostBody() { var context = new DefaultHttpContext(); var body = "malformed"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] public async void ClientId_TooLong() { var context = new DefaultHttpContext(); var longClientId = "x".Repeat(_options.InputLengthRestrictions.ClientId + 1); var body = $"client_id={longClientId}&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion=token"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] public async void ClientAssertion_TooLong() { var context = new DefaultHttpContext(); var longToken = "x".Repeat(_options.InputLengthRestrictions.Jwt + 1); var body = $"client_id=client&client_assertion_type=urn:ietf:params:oauth:client-assertion-type:jwt-bearer&client_assertion={longToken}"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/ClientSecretValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IO; using System.Text; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Validation.Setup; using Microsoft.AspNetCore.Http; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class ClientSecretValidation { private const string Category = "Secrets - Client Secret Validator"; [Fact] [Trait("Category", Category)] public async Task confidential_client_with_correct_secret_should_be_able_to_request_token() { var validator = Factory.CreateClientSecretValidator(); var context = new DefaultHttpContext(); var body = "client_id=roclient&client_secret=secret"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var result = await validator.ValidateAsync(context); result.IsError.Should().BeFalse(); result.Client.ClientId.Should().Be("roclient"); } [Fact] [Trait("Category", Category)] public async Task confidential_client_with_incorrect_secret_should_not_be_able_to_request_token() { var validator = Factory.CreateClientSecretValidator(); var context = new DefaultHttpContext(); var body = "client_id=roclient&client_secret=invalid"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var result = await validator.ValidateAsync(context); result.IsError.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task public_client_without_secret_should_be_able_to_request_token() { var validator = Factory.CreateClientSecretValidator(); var context = new DefaultHttpContext(); var body = "client_id=roclient.public"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var result = await validator.ValidateAsync(context); result.IsError.Should().BeFalse(); result.Client.ClientId.Should().Be("roclient.public"); result.Client.RequireClientSecret.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task implicit_client_without_secret_should_be_able_to_authenticate() { var validator = Factory.CreateClientSecretValidator(); var context = new DefaultHttpContext(); var body = "client_id=client.implicit"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var result = await validator.ValidateAsync(context); result.IsError.Should().BeFalse(); result.Client.ClientId.Should().Be("client.implicit"); } [Fact] [Trait("Category", Category)] public async Task implicit_client_and_client_creds_without_secret_should_not_be_able_to_authenticate() { var validator = Factory.CreateClientSecretValidator(); var context = new DefaultHttpContext(); var body = "client_id=implicit_and_client_creds"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var result = await validator.ValidateAsync(context); result.IsError.Should().BeTrue(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/FormPostCredentialParsing.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.IO; using System.Text; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Validation; using Microsoft.AspNetCore.Http; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class FormPostCredentialExtraction { private const string Category = "Secrets - Form Post Secret Parsing"; private IdentityServerOptions _options; private PostBodySecretParser _parser; public FormPostCredentialExtraction() { _options = new IdentityServerOptions(); _parser = new PostBodySecretParser(_options, new LoggerFactory().CreateLogger()); } [Fact] [Trait("Category", Category)] public async void EmptyContext() { var context = new DefaultHttpContext(); context.Request.Body = new MemoryStream(); var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void Valid_PostBody() { var context = new DefaultHttpContext(); var body = "client_id=client&client_secret=secret"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Type.Should().Be(IdentityServerConstants.ParsedSecretTypes.SharedSecret); secret.Id.Should().Be("client"); secret.Credential.Should().Be("secret"); } [Fact] [Trait("Category", Category)] public async void ClientId_Too_Long() { var context = new DefaultHttpContext(); var longClientId = "x".Repeat(_options.InputLengthRestrictions.ClientId + 1); var body = string.Format("client_id={0}&client_secret=secret", longClientId); context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void ClientSecret_Too_Long() { var context = new DefaultHttpContext(); var longClientSecret = "x".Repeat(_options.InputLengthRestrictions.ClientSecret + 1); var body = string.Format("client_id=client&client_secret={0}", longClientSecret); context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void Missing_ClientId() { var context = new DefaultHttpContext(); var body = "client_secret=secret"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async void Missing_ClientSecret() { var context = new DefaultHttpContext(); var body = "client_id=client"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().NotBeNull(); secret.Type.Should().Be(IdentityServerConstants.ParsedSecretTypes.NoSecret); } [Fact] [Trait("Category", Category)] public async void Malformed_PostBody() { var context = new DefaultHttpContext(); var body = "malformed"; context.Request.Body = new MemoryStream(Encoding.UTF8.GetBytes(body)); context.Request.ContentType = "application/x-www-form-urlencoded"; var secret = await _parser.ParseAsync(context); secret.Should().BeNull(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/HashedSharedSecretValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Validation; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class HashedSharedSecretValidation { private const string Category = "Secrets - Hashed Shared Secret Validation"; private ISecretValidator _validator = new HashedSharedSecretValidator(new Logger(new LoggerFactory())); private IClientStore _clients = new InMemoryClientStore(ClientValidationTestClients.Get()); [Fact] [Trait("Category", Category)] public async Task Valid_Single_Secret() { var clientId = "single_secret_hashed_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Credential_Type() { var clientId = "single_secret_hashed_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = "invalid" }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Multiple_Secrets() { var clientId = "multiple_secrets_hashed"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "foobar"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "quux"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "notexpired"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Single_Secret() { var clientId = "single_secret_hashed_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "invalid", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Multiple_Secrets() { var clientId = "multiple_secrets_hashed"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "invalid", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Client_with_no_Secret_Should_Fail() { var clientId = "no_secret_client"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Client_with_null_Secret_Should_Fail() { var clientId = "null_secret_client"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret, Credential = "secret" }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/MutualTlsSecretValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Validation; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class MutualTlsSecretValidation { private const string Category = "Secrets - MutualTls Secret Validation"; private IClientStore _clients = new InMemoryClientStore(ClientValidationTestClients.Get()); /////////////////// // thumbprints /////////////////// [Fact] [Trait("Category", Category)] public async Task Thumbprint_invalid_secret_type_should_not_match() { ISecretValidator validator = new X509ThumbprintSecretValidator(new Logger(new LoggerFactory())); var clientId = "mtls_client_invalid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Thumbprint_missing_cert_should_throw() { ISecretValidator validator = new X509ThumbprintSecretValidator(new Logger(new LoggerFactory())); var clientId = "mtls_client_invalid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.X509Certificate }; Func act = async () => await validator.ValidateAsync(client.ClientSecrets, secret); await act.Should().ThrowAsync(); } [Fact] [Trait("Category", Category)] public async Task Thumbprint_invalid_secret_should_not_match() { ISecretValidator validator = new X509ThumbprintSecretValidator(new Logger(new LoggerFactory())); var clientId = "mtls_client_invalid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = TestCert.Load(), Type = IdentityServerConstants.ParsedSecretTypes.X509Certificate }; var result = await validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Thumbprint_valid_secret_should_match() { ISecretValidator validator = new X509ThumbprintSecretValidator(new Logger(new LoggerFactory())); var clientId = "mtls_client_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = TestCert.Load(), Type = IdentityServerConstants.ParsedSecretTypes.X509Certificate }; var result = await validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } /////////////////// // names /////////////////// [Fact] [Trait("Category", Category)] public async Task Name_invalid_secret_type_should_not_match() { ISecretValidator validator = new X509NameSecretValidator(new Logger(new LoggerFactory())); var clientId = "mtls_client_invalid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Name_missing_cert_should_throw() { ISecretValidator validator = new X509NameSecretValidator(new Logger(new LoggerFactory())); var clientId = "mtls_client_invalid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.X509Certificate }; Func act = async () => await validator.ValidateAsync(client.ClientSecrets, secret); await act.Should().ThrowAsync(); } [Fact] [Trait("Category", Category)] public async Task Name_invalid_secret_should_not_match() { ISecretValidator validator = new X509NameSecretValidator(new Logger(new LoggerFactory())); var clientId = "mtls_client_invalid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = TestCert.Load(), Type = IdentityServerConstants.ParsedSecretTypes.X509Certificate }; var result = await validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Name_valid_secret_should_match() { ISecretValidator validator = new X509NameSecretValidator(new Logger(new LoggerFactory())); var clientId = "mtls_client_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = TestCert.Load(), Type = IdentityServerConstants.ParsedSecretTypes.X509Certificate }; var result = await validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/PlainTextClientSecretValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Validation; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class PlainTextClientSecretValidation { private const string Category = "Secrets - PlainText Shared Secret Validation"; private ISecretValidator _validator = new PlainTextSharedSecretValidator(new Logger(new LoggerFactory())); private IClientStore _clients = new InMemoryClientStore(ClientValidationTestClients.Get()); [Fact] [Trait("Category", Category)] public async Task Valid_Single_Secret() { var clientId = "single_secret_no_protection_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Credential_Type() { var clientId = "single_secret_no_protection_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = "invalid" }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Multiple_Secrets_No_Protection() { var clientId = "multiple_secrets_no_protection"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "foobar"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "quux"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "notexpired"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Single_Secret() { var clientId = "single_secret_no_protection_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "invalid", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Multiple_Secrets() { var clientId = "multiple_secrets_no_protection"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "invalid", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Client_with_no_Secret_Should_Fail() { var clientId = "no_secret_client"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Client_with_null_Secret_Should_Fail() { var clientId = "null_secret_client"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret, Credential = "secret" }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/PrivateKeyJwtSecretValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.IdentityModel.Tokens.Jwt; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Services.Default; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Stores; using IdentityServer8.Validation; using Microsoft.Extensions.Logging; using Microsoft.IdentityModel.Tokens; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class PrivateKeyJwtSecretValidation { private readonly ISecretValidator _validator; private readonly IClientStore _clients; public PrivateKeyJwtSecretValidation() { _validator = new PrivateKeyJwtSecretValidator( new MockHttpContextAccessor( new IdentityServerOptions() { IssuerUri = "https://idsrv3.com" } ), new DefaultReplayCache(new TestCache()), new LoggerFactory().CreateLogger() ); _clients = new InMemoryClientStore(ClientValidationTestClients.Get()); } private JwtSecurityToken CreateToken(string clientId, DateTime? nowOverride = null) { var certificate = TestCert.Load(); var now = nowOverride ?? DateTime.UtcNow; var token = new JwtSecurityToken( clientId, "https://idsrv3.com/connect/token", new List() { new Claim("jti", Guid.NewGuid().ToString()), new Claim(JwtClaimTypes.Subject, clientId), new Claim(JwtClaimTypes.IssuedAt, new DateTimeOffset(now).ToUnixTimeSeconds().ToString(), ClaimValueTypes.Integer64) }, now, now.AddMinutes(1), new SigningCredentials( new X509SecurityKey(certificate), SecurityAlgorithms.RsaSha256 ) ); return token; } [Fact] public async Task Invalid_Certificate_X5t_Only_Requires_Full_Certificate() { var clientId = "certificate_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var token = CreateToken(clientId); var secret = new ParsedSecret { Id = clientId, Credential = new JwtSecurityTokenHandler().WriteToken(token), Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] public async Task Invalid_Certificate_Thumbprint() { var clientId = "certificate_invalid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId)), Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] public async Task Valid_Certificate_Base64() { var clientId = "certificate_base64_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId)), Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } [Fact] public async Task Invalid_Replay() { var clientId = "certificate_base64_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var token = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId)); var secret = new ParsedSecret { Id = clientId, Credential = token, Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] public async Task Invalid_Certificate_Base64() { var clientId = "certificate_base64_invalid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = new JwtSecurityTokenHandler().WriteToken(CreateToken(clientId)), Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] public async Task Invalid_Issuer() { var clientId = "certificate_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var token = CreateToken(clientId); token.Payload.Remove(JwtClaimTypes.Issuer); token.Payload.Add(JwtClaimTypes.Issuer, "invalid"); var secret = new ParsedSecret { Id = clientId, Credential = new JwtSecurityTokenHandler().WriteToken(token), Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] public async Task Invalid_Subject() { var clientId = "certificate_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var token = CreateToken(clientId); token.Payload.Remove(JwtClaimTypes.Subject); token.Payload.Add(JwtClaimTypes.Subject, "invalid"); var secret = new ParsedSecret { Id = clientId, Credential = new JwtSecurityTokenHandler().WriteToken(token), Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] public async Task Invalid_Expired_Token() { var clientId = "certificate_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var token = CreateToken(clientId, DateTime.UtcNow.AddHours(-1)); var secret = new ParsedSecret { Id = clientId, Credential = new JwtSecurityTokenHandler().WriteToken(token), Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] public async Task Invalid_Unsigned_Token() { var clientId = "certificate_valid"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var token = CreateToken(clientId); token.Header.Remove("alg"); token.Header.Add("alg", "none"); var secret = new ParsedSecret { Id = clientId, Credential = new JwtSecurityTokenHandler().WriteToken(token), Type = IdentityServerConstants.ParsedSecretTypes.JwtBearer }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Secrets/SecretValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Validation; using Microsoft.Extensions.Logging; using Xunit; namespace IdentityServer.UnitTests.Validation.Secrets { public class SecretValidation { private const string Category = "Secrets - Secret Validator"; private ISecretValidator _hashedSecretValidator = new HashedSharedSecretValidator(new Logger(new LoggerFactory())); private IClientStore _clients = new InMemoryClientStore(ClientValidationTestClients.Get()); private SecretValidator _validator; private IdentityServerOptions _options = new IdentityServerOptions(); public SecretValidation() { _validator = new SecretValidator( new StubClock(), new[] { _hashedSecretValidator }, new Logger(new LoggerFactory())); } [Fact] [Trait("Category", Category)] public async Task Valid_Single_Secret() { var clientId = "single_secret_hashed_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Credential_Type() { var clientId = "single_secret_hashed_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = "invalid" }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_Multiple_Secrets() { var clientId = "multiple_secrets_hashed"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "secret", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "foobar"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "quux"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); secret.Credential = "notexpired"; result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Single_Secret() { var clientId = "single_secret_hashed_no_expiration"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "invalid", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Expired_Secret() { var clientId = "multiple_secrets_hashed"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "expired", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Invalid_Multiple_Secrets() { var clientId = "multiple_secrets_hashed"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Credential = "invalid", Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Client_with_no_Secret_Should_Fail() { var clientId = "no_secret_client"; var client = await _clients.FindEnabledClientByIdAsync(clientId); var secret = new ParsedSecret { Id = clientId, Type = IdentityServerConstants.ParsedSecretTypes.SharedSecret }; var result = await _validator.ValidateAsync(client.ClientSecrets, secret); result.Success.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/ClientValidationTestClients.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Security.Cryptography.X509Certificates; using IdentityServer.UnitTests.Common; using IdentityServer8; using IdentityServer8.Models; using static IdentityServer8.IdentityServerConstants; namespace IdentityServer.UnitTests.Validation.Setup { internal static class ClientValidationTestClients { public static List Get() { return new List { new Client { ClientName = "Disabled client", ClientId = "disabled_client", Enabled = false, ClientSecrets = new List { new Secret("secret") } }, new Client { ClientName = "Client with no secret set", ClientId = "no_secret_client", Enabled = true }, new Client { ClientName = "Client with null secret set", ClientId = "null_secret_client", Enabled = true, ClientSecrets = { new Secret(null) } }, new Client { ClientName = "Client with single secret, no protection, no expiration", ClientId = "single_secret_no_protection_no_expiration", Enabled = true, ClientSecrets = new List { new Secret("secret") } }, new Client { ClientName = "Client with X509 Certificate", ClientId = "certificate_valid", Enabled = true, ClientSecrets = new List { new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateThumbprint, Value = TestCert.Load().Thumbprint } } }, new Client { ClientName = "Client with X509 Certificate", ClientId = "certificate_invalid", Enabled = true, ClientSecrets = new List { new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateThumbprint, Value = "invalid" } } }, new Client { ClientName = "Client with Base64 encoded X509 Certificate", ClientId = "certificate_base64_valid", Enabled = true, ClientSecrets = new List { new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = Convert.ToBase64String(TestCert.Load().Export(X509ContentType.Cert)) } } }, new Client { ClientName = "Client with Base64 encoded X509 Certificate", ClientId = "certificate_base64_invalid", Enabled = true, ClientSecrets = new List { new Secret { Type = IdentityServerConstants.SecretTypes.X509CertificateBase64, Value = "invalid" } } }, new Client { ClientName = "Client with single secret, hashed, no expiration", ClientId = "single_secret_hashed_no_expiration", Enabled = true, ClientSecrets = new List { // secret new Secret("secret".Sha256()) } }, new Client { ClientName = "Client with multiple secrets, no protection", ClientId = "multiple_secrets_no_protection", Enabled = true, ClientSecrets = new List { new Secret("secret"), new Secret("foobar", "some description"), new Secret("quux"), new Secret("notexpired", DateTime.UtcNow.AddDays(1)), new Secret("expired", DateTime.UtcNow.AddDays(-1)) } }, new Client { ClientName = "Client with multiple secrets, hashed", ClientId = "multiple_secrets_hashed", Enabled = true, ClientSecrets = new List { // secret new Secret("secret".Sha256()), // foobar new Secret("foobar".Sha256(), "some description"), // quux new Secret("quux".Sha512()), // notexpired new Secret("notexpired".Sha256(), DateTime.UtcNow.AddDays(1)), // expired new Secret("expired".Sha512(), DateTime.UtcNow.AddDays(-1)) }, }, new Client { ClientName = "MTLS Client with invalid secrets", ClientId = "mtls_client_invalid", Enabled = true, ClientSecrets = new List { new Secret(@"CN=invalid", "mtls.test") { Type = SecretTypes.X509CertificateName }, new Secret("invalid", "mtls.test") { Type = SecretTypes.X509CertificateThumbprint }, } }, new Client { ClientName = "MTLS Client with valid secrets", ClientId = "mtls_client_valid", Enabled = true, ClientSecrets = new List { new Secret(@"CN=identityserver_testing", "mtls.test") { Type = SecretTypes.X509CertificateName }, new Secret("4B5FE072C7AD8A9B5DCFDD1A20608BB54DE0954F", "mtls.test") { Type = SecretTypes.X509CertificateThumbprint }, } } }; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/Factory.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Net.Http; using IdentityServer.UnitTests.Common; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Services; using IdentityServer8.Services.Default; using IdentityServer8.Stores; using IdentityServer8.Stores.Serialization; using IdentityServer8.Validation; using Microsoft.AspNetCore.Authentication; using Microsoft.Extensions.Logging; namespace IdentityServer.UnitTests.Validation.Setup { internal static class Factory { public static IClientStore CreateClientStore() { return new InMemoryClientStore(TestClients.Get()); } public static TokenRequestValidator CreateTokenRequestValidator( IdentityServerOptions options = null, IResourceStore resourceStore = null, IAuthorizationCodeStore authorizationCodeStore = null, IRefreshTokenStore refreshTokenStore = null, IResourceOwnerPasswordValidator resourceOwnerValidator = null, IProfileService profile = null, IDeviceCodeValidator deviceCodeValidator = null, IEnumerable extensionGrantValidators = null, ICustomTokenRequestValidator customRequestValidator = null, ITokenValidator tokenValidator = null, IRefreshTokenService refreshTokenService = null, IResourceValidator resourceValidator = null) { if (options == null) { options = TestIdentityServerOptions.Create(); } if (resourceStore == null) { resourceStore = new InMemoryResourcesStore(TestScopes.GetIdentity(), TestScopes.GetApis(), TestScopes.GetScopes()); } if (resourceOwnerValidator == null) { resourceOwnerValidator = new TestResourceOwnerPasswordValidator(); } if (profile == null) { profile = new TestProfileService(); } if (deviceCodeValidator == null) { deviceCodeValidator = new TestDeviceCodeValidator(); } if (customRequestValidator == null) { customRequestValidator = new DefaultCustomTokenRequestValidator(); } ExtensionGrantValidator aggregateExtensionGrantValidator; if (extensionGrantValidators == null) { aggregateExtensionGrantValidator = new ExtensionGrantValidator(new[] { new TestGrantValidator() }, TestLogger.Create()); } else { aggregateExtensionGrantValidator = new ExtensionGrantValidator(extensionGrantValidators, TestLogger.Create()); } if (authorizationCodeStore == null) { authorizationCodeStore = CreateAuthorizationCodeStore(); } if (refreshTokenStore == null) { refreshTokenStore = CreateRefreshTokenStore(); } if (resourceValidator == null) { resourceValidator = CreateResourceValidator(resourceStore); } if (tokenValidator == null) { tokenValidator = CreateTokenValidator(refreshTokenStore: refreshTokenStore, profile: profile); } if (refreshTokenService == null) { refreshTokenService = CreateRefreshTokenService( refreshTokenStore, profile); } return new TokenRequestValidator( options, authorizationCodeStore, resourceOwnerValidator, profile, deviceCodeValidator, aggregateExtensionGrantValidator, customRequestValidator, resourceValidator, resourceStore, tokenValidator, refreshTokenService, new TestEventService(), new StubClock(), TestLogger.Create()); } private static IRefreshTokenService CreateRefreshTokenService(IRefreshTokenStore store, IProfileService profile) { var service = new DefaultRefreshTokenService( store, profile, new StubClock(), TestLogger.Create()); return service; } internal static IResourceValidator CreateResourceValidator(IResourceStore store = null) { store = store ?? new InMemoryResourcesStore(TestScopes.GetIdentity(), TestScopes.GetApis(), TestScopes.GetScopes()); return new DefaultResourceValidator(store, new DefaultScopeParser(TestLogger.Create()), TestLogger.Create()); } internal static ITokenCreationService CreateDefaultTokenCreator(IdentityServerOptions options = null) { return new DefaultTokenCreationService( new StubClock(), new DefaultKeyMaterialService(new IValidationKeysStore[] { }, new ISigningCredentialStore[] { new InMemorySigningCredentialsStore(TestCert.LoadSigningCredentials()) }), options ?? TestIdentityServerOptions.Create(), TestLogger.Create()); } public static DeviceAuthorizationRequestValidator CreateDeviceAuthorizationRequestValidator( IdentityServerOptions options = null, IResourceStore resourceStore = null, IResourceValidator resourceValidator = null) { if (options == null) { options = TestIdentityServerOptions.Create(); } if (resourceStore == null) { resourceStore = new InMemoryResourcesStore(TestScopes.GetIdentity(), TestScopes.GetApis(), TestScopes.GetScopes()); } if (resourceValidator == null) { resourceValidator = CreateResourceValidator(resourceStore); } return new DeviceAuthorizationRequestValidator( options, resourceValidator, TestLogger.Create()); } public static AuthorizeRequestValidator CreateAuthorizeRequestValidator( IdentityServerOptions options = null, IResourceStore resourceStore = null, IClientStore clients = null, IProfileService profile = null, ICustomAuthorizeRequestValidator customValidator = null, IRedirectUriValidator uriValidator = null, IResourceValidator resourceValidator = null, JwtRequestValidator jwtRequestValidator = null, IJwtRequestUriHttpClient jwtRequestUriHttpClient = null) { if (options == null) { options = TestIdentityServerOptions.Create(); } if (resourceStore == null) { resourceStore = new InMemoryResourcesStore(TestScopes.GetIdentity(), TestScopes.GetApis(), TestScopes.GetScopes()); } if (clients == null) { clients = new InMemoryClientStore(TestClients.Get()); } if (customValidator == null) { customValidator = new DefaultCustomAuthorizeRequestValidator(); } if (uriValidator == null) { uriValidator = new StrictRedirectUriValidator(); } if (resourceValidator == null) { resourceValidator = CreateResourceValidator(resourceStore); } if (jwtRequestValidator == null) { jwtRequestValidator = new JwtRequestValidator("https://identityserver", new LoggerFactory().CreateLogger()); } if (jwtRequestUriHttpClient == null) { jwtRequestUriHttpClient = new DefaultJwtRequestUriHttpClient(new HttpClient(new NetworkHandler(new Exception("no jwt request uri response configured"))), options, new LoggerFactory()); } var userSession = new MockUserSession(); return new AuthorizeRequestValidator( options, clients, customValidator, uriValidator, resourceValidator, userSession, jwtRequestValidator, jwtRequestUriHttpClient, TestLogger.Create()); } public static TokenValidator CreateTokenValidator( IReferenceTokenStore store = null, IRefreshTokenStore refreshTokenStore = null, IProfileService profile = null, IdentityServerOptions options = null, ISystemClock clock = null) { if (options == null) { options = TestIdentityServerOptions.Create(); } if (profile == null) { profile = new TestProfileService(); } if (store == null) { store = CreateReferenceTokenStore(); } clock = clock ?? new StubClock(); if (refreshTokenStore == null) { refreshTokenStore = CreateRefreshTokenStore(); } var clients = CreateClientStore(); var context = new MockHttpContextAccessor(options); var logger = TestLogger.Create(); var keyInfo = new SecurityKeyInfo { Key = TestCert.LoadSigningCredentials().Key, SigningAlgorithm = "RS256" }; var validator = new TokenValidator( clients: clients, clock: clock, profile: profile, referenceTokenStore: store, refreshTokenStore: refreshTokenStore, customValidator: new DefaultCustomTokenValidator(), keys: new DefaultKeyMaterialService(new[] { new InMemoryValidationKeysStore(new[] { keyInfo }) }, Enumerable.Empty()), logger: logger, options: options, context: context); return validator; } public static IDeviceCodeValidator CreateDeviceCodeValidator( IDeviceFlowCodeService service, IProfileService profile = null, IDeviceFlowThrottlingService throttlingService = null, ISystemClock clock = null) { profile = profile ?? new TestProfileService(); throttlingService = throttlingService ?? new TestDeviceFlowThrottlingService(); clock = clock ?? new StubClock(); var validator = new DeviceCodeValidator(service, profile, throttlingService, clock, TestLogger.Create()); return validator; } public static IClientSecretValidator CreateClientSecretValidator(IClientStore clients = null, SecretParser parser = null, SecretValidator validator = null, IdentityServerOptions options = null) { options = options ?? TestIdentityServerOptions.Create(); if (clients == null) clients = new InMemoryClientStore(TestClients.Get()); if (parser == null) { var parsers = new List { new BasicAuthenticationSecretParser(options, TestLogger.Create()), new PostBodySecretParser(options, TestLogger.Create()) }; parser = new SecretParser(parsers, TestLogger.Create()); } if (validator == null) { var validators = new List { new HashedSharedSecretValidator(TestLogger.Create()), new PlainTextSharedSecretValidator(TestLogger.Create()) }; validator = new SecretValidator(new StubClock(), validators, TestLogger.Create()); } return new ClientSecretValidator(clients, parser, validator, new TestEventService(), TestLogger.Create()); } public static IAuthorizationCodeStore CreateAuthorizationCodeStore() { return new DefaultAuthorizationCodeStore(new InMemoryPersistedGrantStore(), new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); } public static IRefreshTokenStore CreateRefreshTokenStore() { return new DefaultRefreshTokenStore(new InMemoryPersistedGrantStore(), new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); } public static IReferenceTokenStore CreateReferenceTokenStore() { return new DefaultReferenceTokenStore(new InMemoryPersistedGrantStore(), new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); } public static IDeviceFlowCodeService CreateDeviceCodeService() { return new DefaultDeviceFlowCodeService(new InMemoryDeviceFlowStore(), new DefaultHandleGenerationService()); } public static IUserConsentStore CreateUserConsentStore() { return new DefaultUserConsentStore(new InMemoryPersistedGrantStore(), new PersistentGrantSerializer(), new DefaultHandleGenerationService(), TestLogger.Create()); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TestClients.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using IdentityServer8; using IdentityServer8.Models; namespace IdentityServer.UnitTests.Validation.Setup { internal class TestClients { public static IEnumerable Get() { return new List { new Client { ClientName = "Code Client", Enabled = true, ClientId = "codeclient", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RequireConsent = false, RequirePkce = false, RedirectUris = new List { "https://server/cb" }, AuthorizationCodeLifetime = 60 }, new Client { ClientName = "Code Client (allows plain text PKCE)", Enabled = true, ClientId = "codeclient.plain", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AllowPlainTextPkce = true, RequireConsent = false, RedirectUris = new List { "https://server/cb" }, AuthorizationCodeLifetime = 60 }, new Client { ClientName = "Code Client with PKCE", Enabled = true, ClientId = "codeclient.pkce", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RequireConsent = false, RedirectUris = new List { "https://server/cb" }, AuthorizationCodeLifetime = 60 }, new Client { ClientName = "Code Client with PKCE and plain allowed", Enabled = true, ClientId = "codeclient.pkce.plain", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequirePkce = true, AllowPlainTextPkce = true, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RequireConsent = false, RedirectUris = new List { "https://server/cb" }, AuthorizationCodeLifetime = 60 }, new Client { ClientName = "Hybrid Client", Enabled = true, ClientId = "hybridclient", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Hybrid, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AllowAccessTokensViaBrowser = true, RequireConsent = false, RequirePkce = false, RedirectUris = new List { "https://server/cb" }, AuthorizationCodeLifetime = 60 }, new Client { ClientName = "Hybrid Client with PKCE", Enabled = true, ClientId = "hybridclient.pkce", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Hybrid, RequirePkce = true, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AllowAccessTokensViaBrowser = true, RequireConsent = false, RedirectUris = new List { "https://server/cb" }, AuthorizationCodeLifetime = 60 }, new Client { ClientName = "Hybrid Client", Enabled = true, ClientId = "hybridclient_no_aavb", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Hybrid, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AllowAccessTokensViaBrowser = false, RequireConsent = false, RequirePkce = false, RedirectUris = new List { "https://server/cb" }, AuthorizationCodeLifetime = 60 }, new Client { ClientName = "Implicit Client", ClientId = "implicitclient", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AllowAccessTokensViaBrowser = true, RequireConsent = false, RedirectUris = new List { "oob://implicit/cb" } }, new Client { ClientName = "Implicit Client", ClientId = "implicitclient_no_aavb", AllowedGrantTypes = GrantTypes.Implicit, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AllowAccessTokensViaBrowser = false, RequireConsent = false, RedirectUris = new List { "oob://implicit/cb" } }, new Client { ClientName = "Implicit and Client Credentials Client", Enabled = true, ClientId = "implicit_and_client_creds_client", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RequireConsent = false, RedirectUris = new List { "oob://implicit/cb" } }, new Client { ClientName = "Code Client with Scope Restrictions", Enabled = true, ClientId = "codeclient_restricted", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Code, RequireConsent = false, RequirePkce = false, AllowedScopes = new List { "openid" }, RedirectUris = new List { "https://server/cb" } }, new Client { ClientName = "Client Credentials Client", Enabled = true, ClientId = "client", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AllowOfflineAccess = true, AccessTokenType = AccessTokenType.Jwt }, new Client { ClientName = "Client Credentials Client (restricted)", Enabled = true, ClientId = "client_restricted", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = new List { "resource" } }, new Client { ClientName = "Resource Owner Client", Enabled = true, ClientId = "roclient", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AllowOfflineAccess = true }, new Client { ClientName = "Resource Owner Client - Public", Enabled = true, ClientId = "roclient.public", RequireClientSecret = false, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "openid", "profile", "resource", "resource2" } }, new Client { ClientName = "Resource Owner Client", Enabled = true, ClientId = "roclient_absolute_refresh_expiration_one_time_only", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RefreshTokenExpiration = TokenExpiration.Absolute, RefreshTokenUsage = TokenUsage.OneTimeOnly, AbsoluteRefreshTokenLifetime = 200 }, new Client { ClientName = "Resource Owner Client", Enabled = true, ClientId = "roclient_absolute_refresh_expiration_reuse", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RefreshTokenExpiration = TokenExpiration.Absolute, RefreshTokenUsage = TokenUsage.ReUse, AbsoluteRefreshTokenLifetime = 200 }, new Client { ClientName = "Resource Owner Client", Enabled = true, ClientId = "roclient_sliding_refresh_expiration_one_time_only", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RefreshTokenExpiration = TokenExpiration.Sliding, RefreshTokenUsage = TokenUsage.OneTimeOnly, AbsoluteRefreshTokenLifetime = 10, SlidingRefreshTokenLifetime = 4 }, new Client { ClientName = "Resource Owner Client", Enabled = true, ClientId = "roclient_sliding_refresh_expiration_reuse", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RefreshTokenExpiration = TokenExpiration.Sliding, RefreshTokenUsage = TokenUsage.ReUse, AbsoluteRefreshTokenLifetime = 200, SlidingRefreshTokenLifetime = 100 }, new Client { ClientName = "Resource Owner Client (restricted)", Enabled = true, ClientId = "roclient_restricted", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowedScopes = new List { "resource" } }, new Client { ClientName = "Resource Owner Client (restricted with refresh)", Enabled = true, ClientId = "roclient_restricted_refresh", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.ResourceOwnerPassword, AllowOfflineAccess = true, AllowedScopes = new List { "resource" } }, new Client { ClientName = "Custom Grant Client", Enabled = true, ClientId = "customgrantclient", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = { "custom_grant" }, AllowedScopes = { "openid", "profile", "resource", "resource2" } }, new Client { ClientName = "Disabled Client", Enabled = false, ClientId = "disabled", ClientSecrets = new List { new Secret("invalid".Sha256()) }, AllowedGrantTypes = GrantTypes.ClientCredentials, AllowedScopes = { "openid", "profile", "resource", "resource2" } }, new Client { ClientName = "Reference Token Client", Enabled = true, ClientId = "referencetokenclient", ClientSecrets = new List { new Secret("secret".Sha256()) }, AllowedGrantTypes = GrantTypes.Implicit, RedirectUris = { "https://notused" }, AllowedScopes = { "openid", "profile", "resource", "resource2" }, AccessTokenType = AccessTokenType.Reference }, new Client { ClientId = "wsfed", ClientName = "WS-Fed Client", ProtocolType = IdentityServerConstants.ProtocolTypes.WsFederation, AllowedGrantTypes = GrantTypes.Implicit, Enabled = true, AllowedScopes = { "openid", "profile", "resource", "resource2" }, RedirectUris = { "http://wsfed/callback" } }, new Client { ClientId = "client.cred.wsfed", ClientName = "WS-Fed Client", ProtocolType = IdentityServerConstants.ProtocolTypes.WsFederation, AllowedGrantTypes = GrantTypes.ClientCredentials, ClientSecrets = { new Secret("secret".Sha256()) }, Enabled = true, AllowedScopes = { "openid", "profile", "resource", "resource2" } }, new Client { ClientId = "client.implicit", ClientName = "Implicit Client", AllowedGrantTypes = GrantTypes.Implicit, RedirectUris = { "https://notused" }, AllowedScopes = { "openid", "profile", "resource", "resource2" } }, new Client { ClientId = "implicit_and_client_creds", AllowedGrantTypes = GrantTypes.ImplicitAndClientCredentials, RedirectUris = { "https://notused" }, AllowedScopes = {"api1"} }, new Client { ClientId = "device_flow", ClientName = "Device Flow Client", AllowedGrantTypes = GrantTypes.DeviceFlow, AllowedScopes = { "openid", "profile", "resource" }, AllowOfflineAccess = true, ClientSecrets = new List { new Secret("secret".Sha256()) }, } }; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TestDeviceCodeValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Validation.Setup { public class TestDeviceCodeValidator : IDeviceCodeValidator { private readonly bool shouldError; public TestDeviceCodeValidator(bool shouldError = false) { this.shouldError = shouldError; } public Task ValidateAsync(DeviceCodeValidationContext context) { if (shouldError) context.Result = new TokenRequestValidationResult(context.Request, "error"); else context.Result = new TokenRequestValidationResult(context.Request); return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TestDeviceFlowThrottlingService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Validation.Setup { public class TestDeviceFlowThrottlingService : IDeviceFlowThrottlingService { private readonly bool shouldSlownDown; public TestDeviceFlowThrottlingService(bool shouldSlownDown = false) { this.shouldSlownDown = shouldSlownDown; } public Task ShouldSlowDown(string deviceCode, DeviceCode details) => Task.FromResult(shouldSlownDown); } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TestGrantValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Validation.Setup { internal class TestGrantValidator : IExtensionGrantValidator { private readonly bool _isInvalid; private readonly string _errorDescription; public TestGrantValidator(bool isInvalid = false, string errorDescription = null) { _isInvalid = isInvalid; _errorDescription = errorDescription; } public Task ValidateAsync(ValidatedTokenRequest request) { if (_isInvalid) { return Task.FromResult(new GrantValidationResult(TokenRequestErrors.InvalidGrant, _errorDescription)); } return Task.FromResult(new GrantValidationResult("bob", "CustomGrant")); } public Task ValidateAsync(ExtensionGrantValidationContext context) { if (_isInvalid) { context.Result = new GrantValidationResult(TokenRequestErrors.InvalidGrant, _errorDescription); } else { context.Result = new GrantValidationResult("bob", "CustomGrant"); } return Task.CompletedTask; } public string GrantType { get { return "custom_grant"; } } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TestProfileService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Services; namespace IdentityServer.UnitTests.Validation.Setup { internal class TestProfileService : IProfileService { private bool _shouldBeActive; public TestProfileService(bool shouldBeActive = true) { _shouldBeActive = shouldBeActive; } public Task GetProfileDataAsync(ProfileDataRequestContext context) { return Task.CompletedTask; } public Task IsActiveAsync(IsActiveContext context) { context.IsActive = _shouldBeActive; return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TestResourceOwnerPasswordValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Threading.Tasks; using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Validation.Setup { public class TestResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator { private string _erroDescription; private TokenRequestErrors _error; private readonly bool _sendError; public TestResourceOwnerPasswordValidator() { } public TestResourceOwnerPasswordValidator(TokenRequestErrors error, string errorDescription = null) { _sendError = true; _error = error; _erroDescription = errorDescription; } public Task ValidateAsync(ResourceOwnerPasswordValidationContext context) { if (_sendError) { context.Result = new GrantValidationResult(_error, _erroDescription); return Task.CompletedTask; } if (context.UserName == context.Password) { context.Result = new GrantValidationResult(context.UserName, "password"); } if (context.UserName == "bob_no_password" && context.Password == "") { context.Result = new GrantValidationResult(context.UserName, "password"); } return Task.CompletedTask; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TestScopes.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Generic; using IdentityServer8.Models; namespace IdentityServer.UnitTests.Validation.Setup { internal class TestScopes { public static IEnumerable GetIdentity() { return new IdentityResource[] { new IdentityResources.OpenId(), new IdentityResources.Profile() }; } public static IEnumerable GetApis() { return new ApiResource[] { new ApiResource { Name = "api", Scopes = { "resource", "resource2" } } }; } public static IEnumerable GetScopes() { return new ApiScope[] { new ApiScope { Name = "resource", Description = "resource scope" }, new ApiScope { Name = "resource2", Description = "resource scope" } }; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TestTokenValidator.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Validation; using System.Threading.Tasks; namespace IdentityServer.UnitTests.Validation.Setup { class TestTokenValidator : ITokenValidator { private readonly TokenValidationResult _result; public TestTokenValidator(TokenValidationResult result) { _result = result; } public Task ValidateAccessTokenAsync(string token, string expectedScope = null) { return Task.FromResult(_result); } public Task ValidateIdentityTokenAsync(string token, string clientId = null, bool validateLifetime = true) { return Task.FromResult(_result); } public Task ValidateRefreshTokenAsync(string token, Client client = null) { return Task.FromResult(_result); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/TokenFactory.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Linq; using System.Security.Claims; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer8.Models; namespace IdentityServer.UnitTests.Validation.Setup { internal static class TokenFactory { public static Token CreateAccessToken(Client client, string subjectId, int lifetime, params string[] scopes) { var claims = new List { new Claim("client_id", client.ClientId), new Claim("sub", subjectId) }; scopes.ToList().ForEach(s => claims.Add(new Claim("scope", s))); var token = new Token(OidcConstants.TokenTypes.AccessToken) { CreationTime = DateTime.UtcNow, Audiences = { "https://idsvr.com/resources" }, Issuer = "https://idsvr.com", Lifetime = lifetime, Claims = claims, ClientId = client.ClientId, AccessTokenType = client.AccessTokenType }; return token; } public static Token CreateAccessTokenLong(Client client, string subjectId, int lifetime, int count, params string[] scopes) { var claims = new List { new Claim("client_id", client.ClientId), new Claim("sub", subjectId) }; for (int i = 0; i < count; i++) { claims.Add(new Claim("junk", "x".Repeat(100))); } scopes.ToList().ForEach(s => claims.Add(new Claim("scope", s))); var token = new Token(OidcConstants.TokenTypes.AccessToken) { CreationTime = DateTime.UtcNow, Audiences = { "https://idsvr.com/resources" }, Issuer = "https://idsvr.com", Lifetime = lifetime, Claims = claims, ClientId = client.ClientId, AccessTokenType = client.AccessTokenType }; return token; } public static Token CreateIdentityToken(string clientId, string subjectId) { var clients = Factory.CreateClientStore(); var claims = new List { new Claim("sub", subjectId) }; var token = new Token(OidcConstants.TokenTypes.IdentityToken) { CreationTime = DateTime.UtcNow, Audiences = { clientId }, ClientId = clientId, Issuer = "https://idsvr.com", Lifetime = 600, Claims = claims }; return token; } public static Token CreateIdentityTokenLong(string clientId, string subjectId, int count) { var clients = Factory.CreateClientStore(); var claims = new List { new Claim("sub", subjectId) }; for (int i = 0; i < count; i++) { claims.Add(new Claim("junk", "x".Repeat(100))); } var token = new Token(OidcConstants.TokenTypes.IdentityToken) { CreationTime = DateTime.UtcNow, Audiences = { clientId }, ClientId = clientId, Issuer = "https://idsvr.com", Lifetime = 600, Claims = claims }; return token; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/Setup/ValidationExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using IdentityServer8.Models; using IdentityServer8.Validation; namespace IdentityServer.UnitTests.Validation.Setup { public static class ValidationExtensions { public static ClientSecretValidationResult ToValidationResult(this Client client, ParsedSecret secret = null) { return new ClientSecretValidationResult { Client = client, Secret = secret }; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/StrictRedirectUriValidatorAppAuthValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityServer.UnitTests.Common; using IdentityServer8.Models; using IdentityServer8.Validation; using System.Collections.Generic; using System.Threading.Tasks; using Xunit; namespace IdentityServer.UnitTests.Validation { public class StrictRedirectUriValidatorAppAuthValidation { private const string Category = "Strict Redirect Uri Validator AppAuth Validation Tests"; private Client clientWithValidLoopbackRedirectUri = new Client { RequirePkce = true, RedirectUris = new List { "http://127.0.0.1" } }; private Client clientWithNoRedirectUris = new Client { RequirePkce = true }; [Theory] [Trait("Category", Category)] [InlineData("http://127.0.0.1")] // This is in the clients redirect URIs [InlineData("http://127.0.0.1:0")] [InlineData("http://127.0.0.1:80")] [InlineData("http://127.0.0.1:65535")] [InlineData("http://127.0.0.1:123/a/b")] [InlineData("http://127.0.0.1:123?q=123")] [InlineData("http://127.0.0.1:443/?q=123")] [InlineData("http://127.0.0.1:443/a/b?q=123")] [InlineData("http://127.0.0.1:443#abc")] [InlineData("http://127.0.0.1:443/a/b?q=123#abc")] public async Task Loopback_Redirect_URIs_Should_Be_AllowedAsync(string requestedUri) { var strictRedirectUriValidatorAppAuthValidator = new StrictRedirectUriValidatorAppAuth(TestLogger.Create()); var result = await strictRedirectUriValidatorAppAuthValidator.IsRedirectUriValidAsync(requestedUri, clientWithValidLoopbackRedirectUri); result.Should().BeTrue(); } [Theory] [Trait("Category", Category)] [InlineData(null)] [InlineData("")] [InlineData("http:")] [InlineData("127.0.0.1")] [InlineData("//127.0.0.1")] [InlineData("https://127.0.0.1:123")] [InlineData("http://127.0.0.1:")] [InlineData("http://127.0.0.1:-1")] [InlineData("http://127.0.0.2:65536")] [InlineData("http://127.0.0.1:443a")] [InlineData("http://127.0.0.1:a443")] [InlineData("http://127.0.0.1:443a/")] [InlineData("http://127.0.0.1:443a?")] [InlineData("http://127.0.0.2:443")] [InlineData("http://127.0.0.1#abc")] [InlineData("http://127.0.0.1:#abc")] public async Task Loopback_Redirect_URIs_Should_Not_Be_AllowedAsync(string requestedUri) { var strictRedirectUriValidatorAppAuthValidator = new StrictRedirectUriValidatorAppAuth(TestLogger.Create()); var result = await strictRedirectUriValidatorAppAuthValidator.IsRedirectUriValidAsync(requestedUri, clientWithValidLoopbackRedirectUri); result.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_ClientCredentials_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Linq; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_ClientCredentials_Invalid { private const string Category = "TokenRequest Validation - ClientCredentials - Invalid"; private IClientStore _clients = Factory.CreateClientStore(); [Fact] [Trait("Category", Category)] public async Task Invalid_GrantType_For_Client() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task Request_should_succeed_even_with_allowed_identity_scopes_because_they_are_filtered_out() { var client = await _clients.FindEnabledClientByIdAsync("client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection { { OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials } }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); result.ValidatedRequest.ValidatedResources.Resources.ApiResources.Count.Should().Be(1); result.ValidatedRequest.ValidatedResources.Resources.ApiResources.First().Name.Should().Be("api"); result.ValidatedRequest.ValidatedResources.Resources.ApiScopes.Count.Should().Be(2); result.ValidatedRequest.ValidatedResources.Resources.ApiScopes.Select(x=>x.Name).Should().BeEquivalentTo(new[] { "resource", "resource2" }); } [Fact] [Trait("Category", Category)] public async Task Unknown_Scope() { var client = await _clients.FindEnabledClientByIdAsync("client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "unknown"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Unknown_Scope_Multiple() { var client = await _clients.FindEnabledClientByIdAsync("client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "resource unknown"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Restricted_Scope() { var client = await _clients.FindEnabledClientByIdAsync("client_restricted"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "resource2"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Restricted_Scope_Multiple() { var client = await _clients.FindEnabledClientByIdAsync("client_restricted"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "resource resource2"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Identity_scope_is_not_allowed_for_client_credentials_when_specified_explicitly() { var client = await _clients.FindEnabledClientByIdAsync("client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection { { OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials }, { OidcConstants.TokenRequest.Scope, "openid" } }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Resource_and_Refresh_Token() { var client = await _clients.FindEnabledClientByIdAsync("client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "resource offline_access"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_Code_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_Code_Invalid { private IClientStore _clients = Factory.CreateClientStore(); private const string Category = "TokenRequest Validation - AuthorizationCode - Invalid"; private ClaimsPrincipal _subject = new IdentityServerUser("bob").CreatePrincipal(); [Fact] [Trait("Category", Category)] public async Task Missing_AuthorizationCode() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Invalid_AuthorizationCode() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, "invalid"); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task AuthorizationCodeTooLong() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var options = new IdentityServerOptions(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var longCode = "x".Repeat(options.InputLengthRestrictions.AuthorizationCode + 1); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, longCode); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task No_Scopes_for_AuthorizationCode() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); OidcConstants.TokenErrors.InvalidRequest.Should().Be(result.Error); } [Fact] [Trait("Category", Category)] public async Task Client_Not_Authorized_For_AuthorizationCode_Flow() { var client = await _clients.FindEnabledClientByIdAsync("implicitclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task Client_Trying_To_Request_Token_Using_Another_Clients_Code() { var client1 = await _clients.FindEnabledClientByIdAsync("codeclient"); var client2 = await _clients.FindEnabledClientByIdAsync("codeclient_restricted"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client1.ClientId, Lifetime = client1.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client2.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Missing_RedirectUri() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task Different_RedirectUri_Between_Authorize_And_Token_Request() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server1/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server2/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Expired_AuthorizationCode() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", CreationTime = DateTime.UtcNow.AddSeconds(-100), Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Reused_AuthorizationCode() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("123").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", RequestedScopes = new List { "openid" } }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); // request first time var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); // request second time validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Code_Request_with_disabled_User() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, Subject = new IdentityServerUser("123").CreatePrincipal(), RedirectUri = "https://server/cb", RequestedScopes = new List { "openid" } }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store, profile: new TestProfileService(shouldBeActive: false)); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_DeviceCode_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_DeviceCode_Invalid { private const string Category = "TokenRequest Validation - DeviceCode - Invalid"; private readonly IClientStore _clients = Factory.CreateClientStore(); private readonly DeviceCode deviceCode = new DeviceCode { ClientId = "device_flow", IsAuthorized = true, Subject = new IdentityServerUser("bob").CreatePrincipal(), IsOpenId = true, Lifetime = 300, CreationTime = DateTime.UtcNow, AuthorizedScopes = new[] {"openid", "profile", "resource"} }; [Fact] [Trait("Category", Category)] public async Task Missing_DeviceCode() { var client = await _clients.FindClientByIdAsync("device_flow"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection { {OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.DeviceCode} }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidRequest); } [Fact] [Trait("Category", Category)] public async Task DeviceCode_Too_Long() { var client = await _clients.FindClientByIdAsync("device_flow"); var longCode = "x".Repeat(new IdentityServerOptions().InputLengthRestrictions.AuthorizationCode + 1); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection { {OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.DeviceCode}, {"device_code", longCode} }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Invalid_Grant_For_Client() { var client = await _clients.FindClientByIdAsync("codeclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection { {OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.DeviceCode}, {"device_code", Guid.NewGuid().ToString()} }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task DeviceCodeValidator_Failure() { var client = await _clients.FindClientByIdAsync("device_flow"); var validator = Factory.CreateTokenRequestValidator(deviceCodeValidator: new TestDeviceCodeValidator(true)); var parameters = new NameValueCollection { {OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.DeviceCode}, {"device_code", Guid.NewGuid().ToString()} }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().NotBeNull(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_ExtensionGrants_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_ExtensionGrants_Invalid { private const string Category = "TokenRequest Validation - Extension Grants - Invalid"; private IClientStore _clients = Factory.CreateClientStore(); [Fact] [Trait("Category", Category)] public async Task Invalid_Extension_Grant_Type_For_Client_Credentials_Client() { var client = await _clients.FindEnabledClientByIdAsync("client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection { { OidcConstants.TokenRequest.GrantType, "customGrant" }, { OidcConstants.TokenRequest.Scope, "resource" } }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnsupportedGrantType); } [Fact] [Trait("Category", Category)] public async Task Restricted_Extension_Grant_Type() { var client = await _clients.FindEnabledClientByIdAsync("customgrantclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection { { OidcConstants.TokenRequest.GrantType, "unknown_grant_type" }, { OidcConstants.TokenRequest.Scope, "resource" } }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnsupportedGrantType); } [Fact] [Trait("Category", Category)] public async Task Customer_Error_and_Description_Extension_Grant_Type() { var client = await _clients.FindEnabledClientByIdAsync("customgrantclient"); var validator = Factory.CreateTokenRequestValidator(extensionGrantValidators: new[] { new TestGrantValidator(isInvalid: true, errorDescription: "custom error description") }); var parameters = new NameValueCollection { { OidcConstants.TokenRequest.GrantType, "custom_grant" }, { OidcConstants.TokenRequest.Scope, "resource" } }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); result.ErrorDescription.Should().Be("custom error description"); } [Fact] [Trait("Category", Category)] public async Task inactive_user_should_fail() { var client = await _clients.FindEnabledClientByIdAsync("customgrantclient"); var validator = Factory.CreateTokenRequestValidator( profile: new TestProfileService(shouldBeActive: false)); var parameters = new NameValueCollection { { OidcConstants.TokenRequest.GrantType, "custom_grant" }, { OidcConstants.TokenRequest.Scope, "resource" } }; var result = await validator.ValidateRequestAsync( parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_General_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_General_Invalid { private const string Category = "TokenRequest Validation - General - Invalid"; private IClientStore _clients = new InMemoryClientStore(TestClients.Get()); private ClaimsPrincipal _subject = new IdentityServerUser("bob").CreatePrincipal(); [Fact] [Trait("Category", Category)] public void Parameters_Null() { var validator = Factory.CreateTokenRequestValidator(); Func act = () => validator.ValidateRequestAsync(null, null); act.Should().ThrowAsync(); } [Fact] [Trait("Category", Category)] public void Client_Null() { var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, "valid"); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); Func act = () => validator.ValidateRequestAsync(parameters, null); act.Should().ThrowAsync(); } [Fact] [Trait("Category", Category)] public async Task Unknown_Grant_Type() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "unknown"); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnsupportedGrantType); } [Fact] [Trait("Category", Category)] public async Task Invalid_Protocol_Type() { var client = await _clients.FindEnabledClientByIdAsync("client.cred.wsfed"); var codeStore = Factory.CreateAuthorizationCodeStore(); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore:codeStore); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "client_credentials"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidClient); } [Fact] [Trait("Category", Category)] public async Task Missing_Grant_Type() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var store = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, IsOpenId = true, RedirectUri = "https://server/cb", Subject = _subject }; var handle = await store.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: store); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnsupportedGrantType); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_PKCE.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Text; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_PKCE { private const string Category = "TokenRequest Validation - PKCE"; private IClientStore _clients = Factory.CreateClientStore(); private InputLengthRestrictions lengths = new InputLengthRestrictions(); [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient")] [Trait("Category", Category)] public async Task valid_pkce_token_request_with_plain_method_should_succeed(string clientId) { var client = await _clients.FindEnabledClientByIdAsync(clientId); var grants = Factory.CreateAuthorizationCodeStore(); var verifier = "x".Repeat(lengths.CodeVerifierMinLength); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("bob").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, RedirectUri = "https://server/cb", CodeChallenge = verifier.Sha256(), CodeChallengeMethod = OidcConstants.CodeChallengeMethods.Plain, RequestedScopes = new List { "openid" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.CodeVerifier, verifier); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task valid_pkce_token_request_with_plain_method_should_succeed_hybrid() { var client = await _clients.FindEnabledClientByIdAsync("hybridclient.pkce"); var grants = Factory.CreateAuthorizationCodeStore(); var verifier = "x".Repeat(lengths.CodeVerifierMinLength); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("123").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, RedirectUri = "https://server/cb", CodeChallenge = verifier.Sha256(), CodeChallengeMethod = OidcConstants.CodeChallengeMethods.Plain, RequestedScopes = new List { "openid" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.CodeVerifier, verifier); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient")] [Trait("Category", Category)] public async Task valid_pkce_token_request_with_sha256_method_should_succeed(string clientId) { var client = await _clients.FindEnabledClientByIdAsync(clientId); var grants = Factory.CreateAuthorizationCodeStore(); var verifier = "x".Repeat(lengths.CodeVerifierMinLength); var challenge = VerifierToSha256CodeChallenge(verifier); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("123").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, RedirectUri = "https://server/cb", CodeChallenge = challenge.Sha256(), CodeChallengeMethod = OidcConstants.CodeChallengeMethods.Sha256, RequestedScopes = new List { "openid" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.CodeVerifier, verifier); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Theory] [InlineData("codeclient.pkce")] [Trait("Category", Category)] public async Task token_request_with_missing_code_challenge_and_verifier_should_fail(string clientId) { var client = await _clients.FindEnabledClientByIdAsync(clientId); var grants = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("123").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, RedirectUri = "https://server/cb", RequestedScopes = new List { "openid" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient")] [Trait("Category", Category)] public async Task token_request_with_missing_code_challenge_should_fail(string clientId) { var client = await _clients.FindEnabledClientByIdAsync(clientId); var grants = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("123").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, RedirectUri = "https://server/cb", RequestedScopes = new List { "openid" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.CodeVerifier, "x".Repeat(lengths.CodeVerifierMinLength)); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient")] [Trait("Category", Category)] public async Task token_request_with_invalid_verifier_plain_method_should_fail(string clientId) { var client = await _clients.FindEnabledClientByIdAsync(clientId); var grants = Factory.CreateAuthorizationCodeStore(); var verifier = "x".Repeat(lengths.CodeVerifierMinLength); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("123").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, RedirectUri = "https://server/cb", CodeChallenge = verifier.Sha256(), CodeChallengeMethod = OidcConstants.CodeChallengeMethods.Plain, RequestedScopes = new List { "openid" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.CodeVerifier, verifier + "invalid"); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Theory] [InlineData("codeclient.pkce")] [InlineData("codeclient")] [Trait("Category", Category)] public async Task token_request_with_invalid_verifier_sha256_method_should_fail(string clientId) { var client = await _clients.FindEnabledClientByIdAsync(clientId); var grants = Factory.CreateAuthorizationCodeStore(); var verifier = "x".Repeat(lengths.CodeVerifierMinLength); var challenge = VerifierToSha256CodeChallenge(verifier); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("123").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, RedirectUri = "https://server/cb", CodeChallenge = challenge.Sha256(), CodeChallengeMethod = OidcConstants.CodeChallengeMethods.Sha256, RequestedScopes = new List { "openid" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.CodeVerifier, verifier + "invalid"); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } private static string VerifierToSha256CodeChallenge(string codeVerifier) { var codeVerifierBytes = Encoding.ASCII.GetBytes(codeVerifier); var hashedBytes = codeVerifierBytes.Sha256(); var transformedCodeVerifier = Base64Url.Encode(hashedBytes); return transformedCodeVerifier; } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_RefreshToken_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Configuration; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_RefreshToken_Invalid { private const string Category = "TokenRequest Validation - RefreshToken - Invalid"; private IClientStore _clients = Factory.CreateClientStore(); [Fact] [Trait("Category", Category)] public async Task Non_existing_RefreshToken() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "refresh_token"); parameters.Add(OidcConstants.TokenRequest.RefreshToken, "nonexistent"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task RefreshTokenTooLong() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var options = new IdentityServerOptions(); var validator = Factory.CreateTokenRequestValidator(); var longRefreshToken = "x".Repeat(options.InputLengthRestrictions.RefreshToken + 1); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "refresh_token"); parameters.Add(OidcConstants.TokenRequest.RefreshToken, longRefreshToken); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Expired_RefreshToken() { var refreshToken = new RefreshToken { AccessToken = new Token("access_token") { ClientId = "roclient" }, Lifetime = 10, CreationTime = DateTime.UtcNow.AddSeconds(-15) }; var grants = Factory.CreateRefreshTokenStore(); var handle = await grants.StoreRefreshTokenAsync(refreshToken); var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator( refreshTokenStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "refresh_token"); parameters.Add(OidcConstants.TokenRequest.RefreshToken, handle); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Wrong_Client_Binding_RefreshToken_Request() { var refreshToken = new RefreshToken { AccessToken = new Token("access_token") { ClientId = "otherclient", Lifetime = 600, CreationTime = DateTime.UtcNow } }; var grants = Factory.CreateRefreshTokenStore(); var handle = await grants.StoreRefreshTokenAsync(refreshToken); var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator( refreshTokenStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "refresh_token"); parameters.Add(OidcConstants.TokenRequest.RefreshToken, handle); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Client_has_no_OfflineAccess_Scope_anymore_at_RefreshToken_Request() { var refreshToken = new RefreshToken { AccessToken = new Token("access_token") { ClientId = "roclient_restricted" }, Lifetime = 600, CreationTime = DateTime.UtcNow }; var grants = Factory.CreateRefreshTokenStore(); var handle = await grants.StoreRefreshTokenAsync(refreshToken); var client = await _clients.FindEnabledClientByIdAsync("roclient_restricted"); var validator = Factory.CreateTokenRequestValidator( refreshTokenStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "refresh_token"); parameters.Add(OidcConstants.TokenRequest.RefreshToken, handle); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task RefreshToken_Request_with_disabled_User() { var subjectClaim = new Claim(JwtClaimTypes.Subject, "foo"); var refreshToken = new RefreshToken { AccessToken = new Token("access_token") { Claims = new List { subjectClaim }, ClientId = "roclient" }, Lifetime = 600, CreationTime = DateTime.UtcNow }; var grants = Factory.CreateRefreshTokenStore(); var handle = await grants.StoreRefreshTokenAsync(refreshToken); var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator( refreshTokenStore: grants, profile: new TestProfileService(shouldBeActive: false)); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "refresh_token"); parameters.Add(OidcConstants.TokenRequest.RefreshToken, handle); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_ResourceOwner_Invalid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System.Collections.Specialized; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Common; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Models; using IdentityServer8.Stores; using IdentityServer8.Validation; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_ResourceOwner_Invalid { private const string Category = "TokenRequest Validation - ResourceOwner - Invalid"; private IClientStore _clients = Factory.CreateClientStore(); [Fact] [Trait("Category", Category)] public async Task Invalid_GrantType_For_Client() { var client = await _clients.FindEnabledClientByIdAsync("client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnauthorizedClient); } [Fact] [Trait("Category", Category)] public async Task Unknown_Scope() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "unknown"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Unknown_Scope_Multiple() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource unknown"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Restricted_Scope() { var client = await _clients.FindEnabledClientByIdAsync("roclient_restricted"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource2"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task Restricted_Scope_Multiple() { var client = await _clients.FindEnabledClientByIdAsync("roclient_restricted"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource resource2"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidScope); } [Fact] [Trait("Category", Category)] public async Task No_ResourceOwnerCredentials() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Missing_ResourceOwner_UserName() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Invalid_ResourceOwner_Credentials() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "notbob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); result.ErrorDescription.Should().Be("invalid_username_or_password"); } [Fact] [Trait("Category", Category)] public async Task Missing_ResourceOwner_password_for_user_with_password_should_fail() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob_with_password"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); } [Fact] [Trait("Category", Category)] public async Task Password_GrantType_Not_Supported() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(resourceOwnerValidator: new NotSupportedResourceOwnerPasswordValidator(TestLogger.Create())); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.UnsupportedGrantType); result.ErrorDescription.Should().BeNull(); } [Fact] [Trait("Category", Category)] public async Task Inactive_ResourceOwner() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(profile: new TestProfileService(shouldBeActive: false)); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); } [Fact] [Trait("Category", Category)] public async Task Password_GrantType_With_Custom_ErrorDescription() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(resourceOwnerValidator: new TestResourceOwnerPasswordValidator(TokenRequestErrors.InvalidGrant, "custom error description")); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "notbob"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.TokenErrors.InvalidGrant); result.ErrorDescription.Should().Be("custom error description"); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/TokenRequest Validation/TokenRequestValidation_Valid.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using System; using System.Collections.Generic; using System.Collections.Specialized; using System.Security.Claims; using System.Threading.Tasks; using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8; using IdentityServer8.Models; using IdentityServer8.Stores; using Xunit; namespace IdentityServer.UnitTests.Validation.TokenRequest_Validation { public class TokenRequestValidation_Valid { private const string Category = "TokenRequest Validation - General - Valid"; private IClientStore _clients = Factory.CreateClientStore(); [Fact] [Trait("Category", Category)] public async Task Missing_ResourceOwner_password_for_user_with_no_password_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); parameters.Add(OidcConstants.TokenRequest.UserName, "bob_no_password"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); result.ValidatedRequest.UserName.Should().Be("bob_no_password"); } [Fact] [Trait("Category", Category)] public async Task Valid_code_request_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var grants = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, Subject = new IdentityServerUser("123").CreatePrincipal(), ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, RedirectUri = "https://server/cb", RequestedScopes = new List { "openid" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_code_request_with_refresh_token_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("codeclient"); var grants = Factory.CreateAuthorizationCodeStore(); var code = new AuthorizationCode { CreationTime = DateTime.UtcNow, ClientId = client.ClientId, Lifetime = client.AuthorizationCodeLifetime, Subject = new IdentityServerUser("123").CreatePrincipal(), RedirectUri = "https://server/cb", RequestedScopes = new List { "openid", "offline_access" } }; var handle = await grants.StoreAuthorizationCodeAsync(code); var validator = Factory.CreateTokenRequestValidator( authorizationCodeStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.AuthorizationCode); parameters.Add(OidcConstants.TokenRequest.Code, handle); parameters.Add(OidcConstants.TokenRequest.RedirectUri, "https://server/cb"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_client_credentials_request_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_client_credentials_request_with_default_scopes_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("client_restricted"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_client_credentials_request_for_implicit_and_client_credentials_client_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("implicit_and_client_creds_client"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_client_credentials_request_restricted_client_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("client_restricted"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.ClientCredentials); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_resource_owner_request_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_resource_wwner_request_with_refresh_token_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); parameters.Add(OidcConstants.TokenRequest.Scope, "resource offline_access"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_resource_owner_request_restricted_client_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("roclient_restricted"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.Password); parameters.Add(OidcConstants.TokenRequest.UserName, "bob"); parameters.Add(OidcConstants.TokenRequest.Password, "bob"); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task valid_extension_grant_request_should_succeed() { var client = await _clients.FindEnabledClientByIdAsync("customgrantclient"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "custom_grant"); parameters.Add(OidcConstants.TokenRequest.Scope, "resource"); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_refresh_token_request_should_succeed() { var subjectClaim = new Claim(JwtClaimTypes.Subject, "foo"); var refreshToken = new RefreshToken { AccessToken = new Token("access_token") { Claims = new List { subjectClaim }, ClientId = "roclient" }, Lifetime = 600, CreationTime = DateTime.UtcNow }; var grants = Factory.CreateRefreshTokenStore(); var handle = await grants.StoreRefreshTokenAsync(refreshToken); var client = await _clients.FindEnabledClientByIdAsync("roclient"); var validator = Factory.CreateTokenRequestValidator( refreshTokenStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "refresh_token"); parameters.Add(OidcConstants.TokenRequest.RefreshToken, handle); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_refresh_token_request_using_restricted_client_should_succeed() { var subjectClaim = new Claim(JwtClaimTypes.Subject, "foo"); var refreshToken = new RefreshToken { AccessToken = new Token("access_token") { Claims = new List { subjectClaim }, ClientId = "roclient_restricted_refresh" }, Lifetime = 600, CreationTime = DateTime.UtcNow }; var grants = Factory.CreateRefreshTokenStore(); var handle = await grants.StoreRefreshTokenAsync(refreshToken); var client = await _clients.FindEnabledClientByIdAsync("roclient_restricted_refresh"); var validator = Factory.CreateTokenRequestValidator( refreshTokenStore: grants); var parameters = new NameValueCollection(); parameters.Add(OidcConstants.TokenRequest.GrantType, "refresh_token"); parameters.Add(OidcConstants.TokenRequest.RefreshToken, handle); var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task Valid_device_code_request_should_succeed() { var deviceCode = new DeviceCode { ClientId = "device_flow", IsAuthorized = true, Subject = new IdentityServerUser("bob").CreatePrincipal(), IsOpenId = true, Lifetime = 300, CreationTime = DateTime.UtcNow, AuthorizedScopes = new[] { "openid", "profile", "resource" } }; var client = await _clients.FindClientByIdAsync("device_flow"); var validator = Factory.CreateTokenRequestValidator(); var parameters = new NameValueCollection { {OidcConstants.TokenRequest.GrantType, OidcConstants.GrantTypes.DeviceCode}, {"device_code", Guid.NewGuid().ToString()} }; var result = await validator.ValidateRequestAsync(parameters, client.ToValidationResult()); result.IsError.Should().BeFalse(); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/Validation/UserInfoRequestValidation.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using IdentityModel; using IdentityServer.UnitTests.Validation.Setup; using IdentityServer8.Stores; using IdentityServer8.Validation; using System.Collections.Generic; using System.Security.Claims; using System.Threading.Tasks; using IdentityServer.UnitTests.Common; using Xunit; namespace IdentityServer.UnitTests.Validation { public class UserInfoRequestValidation { private const string Category = "UserInfo Request Validation Tests"; private IClientStore _clients = new InMemoryClientStore(TestClients.Get()); [Fact] [Trait("Category", Category)] public async Task token_without_sub_should_fail() { var tokenResult = new TokenValidationResult { IsError = false, Client = await _clients.FindEnabledClientByIdAsync("codeclient"), Claims = new List() }; var validator = new UserInfoRequestValidator( new TestTokenValidator(tokenResult), new TestProfileService(shouldBeActive: true), TestLogger.Create()); var result = await validator.ValidateRequestAsync("token"); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } [Fact] [Trait("Category", Category)] public async Task active_user_should_succeed() { var tokenResult = new TokenValidationResult { IsError = false, Client = await _clients.FindEnabledClientByIdAsync("codeclient"), Claims = new List { new Claim("sub", "123") }, }; var validator = new UserInfoRequestValidator( new TestTokenValidator(tokenResult), new TestProfileService(shouldBeActive: true), TestLogger.Create()); var result = await validator.ValidateRequestAsync("token"); result.IsError.Should().BeFalse(); } [Fact] [Trait("Category", Category)] public async Task inactive_user_should_fail() { var tokenResult = new TokenValidationResult { IsError = false, Client = await _clients.FindEnabledClientByIdAsync("codeclient"), Claims = new List { new Claim("sub", "123") }, }; var validator = new UserInfoRequestValidator( new TestTokenValidator(tokenResult), new TestProfileService(shouldBeActive: false), TestLogger.Create()); var result = await validator.ValidateRequestAsync("token"); result.IsError.Should().BeTrue(); result.Error.Should().Be(OidcConstants.ProtectedResourceErrors.InvalidToken); } } } ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/identityserver_testing.cer ================================================ -----BEGIN CERTIFICATE----- MIIDQTCCAimgAwIBAgIQO+9qzaJY9I5Ewq81kNEKWTANBgkqhkiG9w0BAQsFADAh MR8wHQYDVQQDDBZpZGVudGl0eXNlcnZlcl90ZXN0aW5nMCAXDTIwMDEyMjIzMTYz OFoYDzIxMDMwNTIyMjMyNjM5WjAhMR8wHQYDVQQDDBZpZGVudGl0eXNlcnZlcl90 ZXN0aW5nMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuyZQvE+6gEoB a+LV3dxjk/Xj4DRJ/tefQKR2AZoYAAfKN4ditFmEfRC1A6rckBpMVihTeb1kwwQT 7H4HTS6O/ERHVjOdghoOjEsVakWhAkvh8gphC4IU0upXlYqMh2WzgXEXwYRFB9Tk 7zoHb1/zjEEAhCf2Xbi6YslBoU71bFWyAaeOTl859wV6WaiBIK5L8nJUaIaq4zmC k8caPZq5E867mZiMdL4TcW9/YoAAO96Wa/W9o6OiuZrP414TlEjVuccpLXvjk0hB U5OZD2bTvD3MQZu1n1QMLwXfaOBrcv1/RqYJkK7vpP6Pp1YYlo7b2PBDVAIrRSVT laViBP0owQIDAQABo3MwcTAOBgNVHQ8BAf8EBAMCBaAwHQYDVR0lBBYwFAYIKwYB BQUHAwIGCCsGAQUFBwMBMCEGA1UdEQQaMBiCFmlkZW50aXR5c2VydmVyX3Rlc3Rp bmcwHQYDVR0OBBYEFCVXNpKp8QlHywXEBFfpXxWcOMcsMA0GCSqGSIb3DQEBCwUA A4IBAQBsEAzwyN6V6N5ggN9G1O0ZpviSjixGkWtySNCBjbGXAhOvfW4M3ysNkDwZ ltk/Q17ihZzw135MrDCmnr5pRiN4CbEGbe1qsb+Z0uCCn8/WcIVYYooW66H/Jez+ dg5RxUukA67ZDnjzRskIer7L2t1C4pDqpvPVcneUxkiYDSgcKpTuCVjkPNQKQTIw Sm98NkQG8G8V8+ENIU837ytkiC5nqQa4zDRHexzWrYhiuayWWxJKcNRVF9YaE8ts vp5N1ewmWbSgF8caJuKraVOISj9R4iqf0XuhfSpW/7eIWYmXfqy/UloeqlALfP5C 2d2FdDSfsQ4Jgc3ebrECAQaCC3Gq -----END CERTIFICATE----- ================================================ FILE: src/IdentityServer8/test/IdentityServer.UnitTests/xunit.runner.json ================================================ { "methodDisplay":"classAndMethod" } ================================================ FILE: src/IdentityServer8.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio Version 17 VisualStudioVersion = 17.8.34322.80 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{5461C61B-B06E-46BA-B206-925B660BE727}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "test", "test", "{45C22EDD-91B1-4AEF-8620-77F4E3E7C544}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8", "IdentityServer8\src\IdentityServer8.csproj", "{407C030E-60E6-41F7-AF43-0AC48EDCC17D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "IdentityServer8\host\Host.csproj", "{784B3C88-30FA-415D-B99E-584063064508}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer.UnitTests", "IdentityServer8\test\IdentityServer.UnitTests\IdentityServer.UnitTests.csproj", "{4291820C-735F-4776-8BC4-6527433BC683}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer.IntegrationTests", "IdentityServer8\test\IdentityServer.IntegrationTests\IdentityServer.IntegrationTests.csproj", "{94501373-478A-478D-8C17-6AC52E3438CF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework.Storage", "EntityFramework.Storage\src\IdentityServer8.EntityFramework.Storage.csproj", "{5302DAB3-1662-4956-97AA-5EA5E85B10F6}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework.UnitTests", "EntityFramework.Storage\test\UnitTests\IdentityServer8.EntityFramework.UnitTests.csproj", "{8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework.IntegrationTests", "EntityFramework.Storage\test\IntegrationTests\IdentityServer8.EntityFramework.IntegrationTests.csproj", "{E90F7470-C7F8-464B-9C28-87C474085812}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "migrations", "migrations", "{E3EF31E0-6658-4899-8BDA-FF84355E2FDD}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlServer", "EntityFramework.Storage\migrations\SqlServer\SqlServer.csproj", "{A93A59EC-D75D-44E1-9720-F75D9EF95BC3}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "host", "host", "{07C56E10-A807-4372-ACD9-ADED2D099BC8}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "ConsoleHost", "EntityFramework.Storage\host\ConsoleHost\ConsoleHost.csproj", "{2AA5AC6B-531B-426E-AD38-5B03F1949CF5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IdentityServer", "IdentityServer", "{37FCD1F7-B8CB-4D7B-A2E8-0AE458652314}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EntityFramework.Storage", "EntityFramework.Storage", "{28C914A4-BC3B-4D55-8799-C8A4A6AD724E}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "IdentityServer", "IdentityServer", "{E5F41733-E9B8-4EA8-A5A3-620827B68340}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EntityFramework.Storage", "EntityFramework.Storage", "{6F2785D9-4208-4A9F-85B0-674185D393C5}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storage", "Storage", "{19B12297-DCC0-4529-A04C-5B0E2C0F01B9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Storage", "Storage", "{6CB8A0D7-77D4-411B-953C-A4AB50F56205}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.Storage", "Storage\src\IdentityServer8.Storage.csproj", "{A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.AspNetIdentity", "AspNetIdentity\src\IdentityServer8.AspNetIdentity.csproj", "{A2CDBE15-5898-4A04-94DC-133EE03A78B3}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "AspNetIdentity\host\Host.csproj", "{B7FBA07C-A462-4869-AF94-5E36DFC3742D}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlServer", "AspNetIdentity\migrations\SqlServer\SqlServer.csproj", "{DD44B9E9-C32E-4F89-92C9-25444A709BBB}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework", "EntityFramework\src\IdentityServer8.EntityFramework.csproj", "{AB83F911-8B78-4973-A3A9-2A2D85581F25}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SqlServer", "EntityFramework\migrations\SqlServer\SqlServer.csproj", "{D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.EntityFramework.Tests", "EntityFramework\test\IdentityServer8.EntityFramework.Tests\IdentityServer8.EntityFramework.Tests.csproj", "{E3685B06-F135-4318-B841-889C35479D5C}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Files", "Solution Files", "{29DC1E06-3EC5-4F52-9EC1-0363BD571369}" ProjectSection(SolutionItems) = preProject ..\.gitignore = ..\.gitignore ..\Directory.Build.props = ..\Directory.Build.props ..\Directory.Build.targets = ..\Directory.Build.targets Directory.BuildOld.targets = Directory.BuildOld.targets ..\Directory.Packages.props = ..\Directory.Packages.props ..\global.json = ..\global.json ..\IdentityServer8.DotNet.ruleset = ..\IdentityServer8.DotNet.ruleset ..\LICENSE = ..\LICENSE ..\LicenseHeader.txt = ..\LicenseHeader.txt ..\NuGet.config = ..\NuGet.config ..\README.md = ..\README.md ..\SECURITY.MD = ..\SECURITY.MD ..\SPONSORS.md = ..\SPONSORS.md ..\version.json = ..\version.json EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "AspNetIdentity", "AspNetIdentity", "{7849D7DE-FD6A-4A5B-A793-5DCFFCDDC944}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EntityFramework", "EntityFramework", "{06FC2C45-FCD6-469C-8F2D-3E1A750D1EF9}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "EntityFramework", "EntityFramework", "{39F7E2E8-1EAB-4163-8E4D-43CBB23AB871}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{A7171FEC-FEC1-4AF0-9625-E69D93F08A42}" ProjectSection(SolutionItems) = preProject ..\.github\CONTRIBUTING.md = ..\.github\CONTRIBUTING.md ..\.github\dependabot.yml = ..\.github\dependabot.yml ..\.github\FUNDING.yml = ..\.github\FUNDING.yml ..\.github\PULL_REQUEST_TEMPLATE.md = ..\.github\PULL_REQUEST_TEMPLATE.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "issuetemplate", "issuetemplate", "{0BC5E76A-A280-49C8-8E96-A43FEA357A4B}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "workflows", "workflows", "{C5474352-0715-41C0-92C2-BABA1A4103A0}" ProjectSection(SolutionItems) = preProject ..\.github\workflows\codeql.yml = ..\.github\workflows\codeql.yml ..\.github\workflows\develop.yml = ..\.github\workflows\develop.yml ..\.github\workflows\master.yml = ..\.github\workflows\master.yml ..\.github\workflows\pre-release.yml = ..\.github\workflows\pre-release.yml ..\.github\workflows\release.yml = ..\.github\workflows\release.yml EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "docs", "docs", "{21B3EAAF-1A7D-4D46-AB3F-843296EDBDC2}" ProjectSection(SolutionItems) = preProject ..\docs\CHANGELOG.md = ..\docs\CHANGELOG.md EndProjectSection EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Security", "Security", "{9B9C47C4-560E-4F2E-8F53-97F9FDE46008}" EndProject Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Security", "Security", "{19B652D0-0AA1-415C-AA62-065EE8C77182}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.Security", "Security\IdentityServer8.Security\IdentityServer8.Security.csproj", "{284DF9E0-8007-4E84-949D-5B610D76B1CF}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.Sanitizer.Tests", "Security\test\IdentityServer8.Santizer.Tests\IdentityServer8.Sanitizer.Tests.csproj", "{00BDF864-B06A-4A48-B8B4-85C219B33CD1}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Host", "EntityFramework\host\Host.csproj", "{C7939ADD-36C9-444F-A773-28EC81587831}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Release|Any CPU = Release|Any CPU EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {407C030E-60E6-41F7-AF43-0AC48EDCC17D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {407C030E-60E6-41F7-AF43-0AC48EDCC17D}.Debug|Any CPU.Build.0 = Debug|Any CPU {407C030E-60E6-41F7-AF43-0AC48EDCC17D}.Release|Any CPU.ActiveCfg = Release|Any CPU {407C030E-60E6-41F7-AF43-0AC48EDCC17D}.Release|Any CPU.Build.0 = Release|Any CPU {784B3C88-30FA-415D-B99E-584063064508}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {784B3C88-30FA-415D-B99E-584063064508}.Debug|Any CPU.Build.0 = Debug|Any CPU {784B3C88-30FA-415D-B99E-584063064508}.Release|Any CPU.ActiveCfg = Release|Any CPU {784B3C88-30FA-415D-B99E-584063064508}.Release|Any CPU.Build.0 = Release|Any CPU {4291820C-735F-4776-8BC4-6527433BC683}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {4291820C-735F-4776-8BC4-6527433BC683}.Debug|Any CPU.Build.0 = Debug|Any CPU {4291820C-735F-4776-8BC4-6527433BC683}.Release|Any CPU.ActiveCfg = Release|Any CPU {4291820C-735F-4776-8BC4-6527433BC683}.Release|Any CPU.Build.0 = Release|Any CPU {94501373-478A-478D-8C17-6AC52E3438CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {94501373-478A-478D-8C17-6AC52E3438CF}.Debug|Any CPU.Build.0 = Debug|Any CPU {94501373-478A-478D-8C17-6AC52E3438CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {94501373-478A-478D-8C17-6AC52E3438CF}.Release|Any CPU.Build.0 = Release|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Debug|Any CPU.Build.0 = Debug|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Release|Any CPU.ActiveCfg = Release|Any CPU {5302DAB3-1662-4956-97AA-5EA5E85B10F6}.Release|Any CPU.Build.0 = Release|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Debug|Any CPU.Build.0 = Debug|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Release|Any CPU.ActiveCfg = Release|Any CPU {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC}.Release|Any CPU.Build.0 = Release|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Debug|Any CPU.Build.0 = Debug|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Release|Any CPU.ActiveCfg = Release|Any CPU {E90F7470-C7F8-464B-9C28-87C474085812}.Release|Any CPU.Build.0 = Release|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Debug|Any CPU.Build.0 = Debug|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Release|Any CPU.ActiveCfg = Release|Any CPU {A93A59EC-D75D-44E1-9720-F75D9EF95BC3}.Release|Any CPU.Build.0 = Release|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Debug|Any CPU.Build.0 = Debug|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Release|Any CPU.ActiveCfg = Release|Any CPU {2AA5AC6B-531B-426E-AD38-5B03F1949CF5}.Release|Any CPU.Build.0 = Release|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Release|Any CPU.Build.0 = Release|Any CPU {A2CDBE15-5898-4A04-94DC-133EE03A78B3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A2CDBE15-5898-4A04-94DC-133EE03A78B3}.Debug|Any CPU.Build.0 = Debug|Any CPU {A2CDBE15-5898-4A04-94DC-133EE03A78B3}.Release|Any CPU.ActiveCfg = Release|Any CPU {A2CDBE15-5898-4A04-94DC-133EE03A78B3}.Release|Any CPU.Build.0 = Release|Any CPU {B7FBA07C-A462-4869-AF94-5E36DFC3742D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {B7FBA07C-A462-4869-AF94-5E36DFC3742D}.Debug|Any CPU.Build.0 = Debug|Any CPU {B7FBA07C-A462-4869-AF94-5E36DFC3742D}.Release|Any CPU.ActiveCfg = Release|Any CPU {B7FBA07C-A462-4869-AF94-5E36DFC3742D}.Release|Any CPU.Build.0 = Release|Any CPU {DD44B9E9-C32E-4F89-92C9-25444A709BBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {DD44B9E9-C32E-4F89-92C9-25444A709BBB}.Debug|Any CPU.Build.0 = Debug|Any CPU {DD44B9E9-C32E-4F89-92C9-25444A709BBB}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB83F911-8B78-4973-A3A9-2A2D85581F25}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {AB83F911-8B78-4973-A3A9-2A2D85581F25}.Debug|Any CPU.Build.0 = Debug|Any CPU {AB83F911-8B78-4973-A3A9-2A2D85581F25}.Release|Any CPU.ActiveCfg = Release|Any CPU {AB83F911-8B78-4973-A3A9-2A2D85581F25}.Release|Any CPU.Build.0 = Release|Any CPU {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}.Debug|Any CPU.Build.0 = Debug|Any CPU {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}.Release|Any CPU.ActiveCfg = Release|Any CPU {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69}.Release|Any CPU.Build.0 = Release|Any CPU {E3685B06-F135-4318-B841-889C35479D5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E3685B06-F135-4318-B841-889C35479D5C}.Debug|Any CPU.Build.0 = Debug|Any CPU {E3685B06-F135-4318-B841-889C35479D5C}.Release|Any CPU.ActiveCfg = Release|Any CPU {E3685B06-F135-4318-B841-889C35479D5C}.Release|Any CPU.Build.0 = Release|Any CPU {284DF9E0-8007-4E84-949D-5B610D76B1CF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {284DF9E0-8007-4E84-949D-5B610D76B1CF}.Debug|Any CPU.Build.0 = Debug|Any CPU {284DF9E0-8007-4E84-949D-5B610D76B1CF}.Release|Any CPU.ActiveCfg = Release|Any CPU {284DF9E0-8007-4E84-949D-5B610D76B1CF}.Release|Any CPU.Build.0 = Release|Any CPU {00BDF864-B06A-4A48-B8B4-85C219B33CD1}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {00BDF864-B06A-4A48-B8B4-85C219B33CD1}.Debug|Any CPU.Build.0 = Debug|Any CPU {00BDF864-B06A-4A48-B8B4-85C219B33CD1}.Release|Any CPU.ActiveCfg = Release|Any CPU {00BDF864-B06A-4A48-B8B4-85C219B33CD1}.Release|Any CPU.Build.0 = Release|Any CPU {C7939ADD-36C9-444F-A773-28EC81587831}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {C7939ADD-36C9-444F-A773-28EC81587831}.Debug|Any CPU.Build.0 = Debug|Any CPU {C7939ADD-36C9-444F-A773-28EC81587831}.Release|Any CPU.ActiveCfg = Release|Any CPU {C7939ADD-36C9-444F-A773-28EC81587831}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {407C030E-60E6-41F7-AF43-0AC48EDCC17D} = {E5F41733-E9B8-4EA8-A5A3-620827B68340} {784B3C88-30FA-415D-B99E-584063064508} = {E5F41733-E9B8-4EA8-A5A3-620827B68340} {4291820C-735F-4776-8BC4-6527433BC683} = {37FCD1F7-B8CB-4D7B-A2E8-0AE458652314} {94501373-478A-478D-8C17-6AC52E3438CF} = {37FCD1F7-B8CB-4D7B-A2E8-0AE458652314} {5302DAB3-1662-4956-97AA-5EA5E85B10F6} = {6F2785D9-4208-4A9F-85B0-674185D393C5} {8239FC82-46A3-4F21-8D05-1F0BE0B1B1FC} = {28C914A4-BC3B-4D55-8799-C8A4A6AD724E} {E90F7470-C7F8-464B-9C28-87C474085812} = {28C914A4-BC3B-4D55-8799-C8A4A6AD724E} {E3EF31E0-6658-4899-8BDA-FF84355E2FDD} = {6F2785D9-4208-4A9F-85B0-674185D393C5} {A93A59EC-D75D-44E1-9720-F75D9EF95BC3} = {E3EF31E0-6658-4899-8BDA-FF84355E2FDD} {07C56E10-A807-4372-ACD9-ADED2D099BC8} = {6F2785D9-4208-4A9F-85B0-674185D393C5} {2AA5AC6B-531B-426E-AD38-5B03F1949CF5} = {07C56E10-A807-4372-ACD9-ADED2D099BC8} {37FCD1F7-B8CB-4D7B-A2E8-0AE458652314} = {45C22EDD-91B1-4AEF-8620-77F4E3E7C544} {28C914A4-BC3B-4D55-8799-C8A4A6AD724E} = {45C22EDD-91B1-4AEF-8620-77F4E3E7C544} {E5F41733-E9B8-4EA8-A5A3-620827B68340} = {5461C61B-B06E-46BA-B206-925B660BE727} {6F2785D9-4208-4A9F-85B0-674185D393C5} = {5461C61B-B06E-46BA-B206-925B660BE727} {19B12297-DCC0-4529-A04C-5B0E2C0F01B9} = {5461C61B-B06E-46BA-B206-925B660BE727} {6CB8A0D7-77D4-411B-953C-A4AB50F56205} = {45C22EDD-91B1-4AEF-8620-77F4E3E7C544} {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7} = {19B12297-DCC0-4529-A04C-5B0E2C0F01B9} {A2CDBE15-5898-4A04-94DC-133EE03A78B3} = {7849D7DE-FD6A-4A5B-A793-5DCFFCDDC944} {B7FBA07C-A462-4869-AF94-5E36DFC3742D} = {7849D7DE-FD6A-4A5B-A793-5DCFFCDDC944} {DD44B9E9-C32E-4F89-92C9-25444A709BBB} = {7849D7DE-FD6A-4A5B-A793-5DCFFCDDC944} {AB83F911-8B78-4973-A3A9-2A2D85581F25} = {06FC2C45-FCD6-469C-8F2D-3E1A750D1EF9} {D9FABEC8-09DC-4B0D-80D7-86FA6C5D9C69} = {06FC2C45-FCD6-469C-8F2D-3E1A750D1EF9} {E3685B06-F135-4318-B841-889C35479D5C} = {39F7E2E8-1EAB-4163-8E4D-43CBB23AB871} {7849D7DE-FD6A-4A5B-A793-5DCFFCDDC944} = {5461C61B-B06E-46BA-B206-925B660BE727} {06FC2C45-FCD6-469C-8F2D-3E1A750D1EF9} = {5461C61B-B06E-46BA-B206-925B660BE727} {39F7E2E8-1EAB-4163-8E4D-43CBB23AB871} = {45C22EDD-91B1-4AEF-8620-77F4E3E7C544} {A7171FEC-FEC1-4AF0-9625-E69D93F08A42} = {29DC1E06-3EC5-4F52-9EC1-0363BD571369} {0BC5E76A-A280-49C8-8E96-A43FEA357A4B} = {A7171FEC-FEC1-4AF0-9625-E69D93F08A42} {C5474352-0715-41C0-92C2-BABA1A4103A0} = {A7171FEC-FEC1-4AF0-9625-E69D93F08A42} {21B3EAAF-1A7D-4D46-AB3F-843296EDBDC2} = {29DC1E06-3EC5-4F52-9EC1-0363BD571369} {9B9C47C4-560E-4F2E-8F53-97F9FDE46008} = {5461C61B-B06E-46BA-B206-925B660BE727} {19B652D0-0AA1-415C-AA62-065EE8C77182} = {45C22EDD-91B1-4AEF-8620-77F4E3E7C544} {284DF9E0-8007-4E84-949D-5B610D76B1CF} = {9B9C47C4-560E-4F2E-8F53-97F9FDE46008} {00BDF864-B06A-4A48-B8B4-85C219B33CD1} = {19B652D0-0AA1-415C-AA62-065EE8C77182} {C7939ADD-36C9-444F-A773-28EC81587831} = {06FC2C45-FCD6-469C-8F2D-3E1A750D1EF9} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {176723F7-4A9B-4F05-A9F3-3BA715E4BDC3} EndGlobalSection EndGlobal ================================================ FILE: src/IdentityServer8.sln.licenseheader ================================================ extensions: designer.cs generated.cs extensions: .cs /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ ================================================ FILE: src/Security/Directory.Build.props ================================================ ================================================ FILE: src/Security/IdentityServer8.Security/Extensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; public class Ioc { static Ioc() { var services = new ServiceCollection(); services.AddLogging(); services.AddSanitizers(); services.AddAllowAnyRedirectService(); services.AddSingleton(); ServiceProvider = services.BuildServiceProvider(); var sanitizer = ServiceProvider.GetRequiredService(); Sanitizer = sanitizer; var redirectService = ServiceProvider.GetRequiredService(); RedirectService = redirectService; } public static ServiceProvider ServiceProvider { get; } public static ISanitizer Sanitizer { get; } public static IRedirectService RedirectService { get; } } ================================================ FILE: src/Security/IdentityServer8.Security/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityServer8.Security; global using Microsoft.AspNetCore.Http; global using Microsoft.Extensions.DependencyInjection; global using Microsoft.Extensions.Logging; global using System.Net; global using System.Web; ================================================ FILE: src/Security/IdentityServer8.Security/IdentityServer8.Security.csproj ================================================ Security package for user input sanitzation IdentityServer8 services and packages. true true enable enable ================================================ FILE: src/Security/IdentityServer8.Security/RedirectService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Security; public interface IRedirectService { bool IsRedirectAllowed(string redirectUrl); } public class RuleMatcher { public bool IsMatch(string url, RedirectRule rule) { throw new NotImplementedException(); } } public class RedirectRule { public static RedirectRule AllowAny => new RedirectRule(); public RedirectRule() { AllowedFragment = Fragment.Any; AllowedPath = Path.Any; AllowedPort = Port.Any; AllowedQuery = Query.Any; AllowedScheme = Scheme.Any; AllowedHost = Host.Any; } public Scheme AllowedScheme { get; set; } public Host AllowedHost { get; set; } public Port AllowedPort { get; set; } public Path AllowedPath { get; set; } public Query AllowedQuery { get; set; } public Fragment AllowedFragment { get; set; } } public class Scheme { public Scheme(string name) { Name = name; } public string Name { get; } public static readonly Scheme Http = new Scheme("http"); public static readonly Scheme Https = new Scheme("https"); public static readonly Scheme Any = new Scheme("*"); public static Scheme Parse(string schemeName) { if (string.IsNullOrWhiteSpace(schemeName)) { throw new ArgumentException("Scheme name cannot be null or empty"); } else if (schemeName.Equals("http", StringComparison.OrdinalIgnoreCase)) { return Http; } else if (schemeName.Equals("https", StringComparison.OrdinalIgnoreCase)) { return Https; } else if (schemeName.Equals("*", StringComparison.OrdinalIgnoreCase)) { return Any; } else { throw new ArgumentException("Invalid scheme name"); } } public override string ToString() { return Name; } } public class Host { public Host(string value) { Value = ((value ?? "").ToLower().Trim() ?? ""); var domainParts = Value.Split('.'); domainParts = domainParts.Where(x => !string.IsNullOrEmpty(x)).ToArray(); HostParts = new List(domainParts); } public List HostParts; public string Value { get; } public static readonly Host Any = new Host("*"); public static Host Create(string hostname) => new Host(hostname); public bool HasWildcard => Value.Contains("*."); public bool IsAny => Value == "*"; public bool IsLocalhost => Value.Equals("localhost", StringComparison.OrdinalIgnoreCase) || Value.Equals("127.0.0.1"); public bool IsIpAddress => Uri.CheckHostName(Value) == UriHostNameType.IPv4 || Uri.CheckHostName(Value) == UriHostNameType.IPv6; public bool IsLocalNetwork => IsLocalhost || IsIpAddress; public bool IsFullQualifiedDomainName => !IsLocalNetwork && !HasWildcard && !IsAny && Value.IndexOf('.') > -1; } public class Port { public Port(int value) { Value = value; } public int Value { get; } public static readonly Port Any = new Port(-1); // Assuming -1 signifies any port public static Port Create(int portNumber) => new Port(portNumber); } public class Path { public Path(string value) { Value = value; } public string Value { get; } public static readonly Path Any = new Path("*"); public static Path Create(string path) => new Path(path); } public class Query { public Query(string value) { Value = value; } public string Value { get; } public static readonly Query Any = new Query("*"); public static Query Create(string query) => new Query(query); } public class Fragment { public Fragment(string value) { Value = value; } public string Value { get; } public static readonly Fragment Any = new Fragment("*"); public static Fragment Create(string fragment) => new Fragment(fragment); } public class RedirectValidition { static RedirectValidition() { var provider = new ServiceCollection() .AddLogging() .AddSingleton() .AddSanitizers() .BuildServiceProvider(); ServiceProvider = provider; } public static ServiceProvider ServiceProvider { get; } } public static class RedirectServiceExtensions { public static IServiceCollection AddAllowAnyRedirectService(this IServiceCollection services) { services.AddSingleton(); return services; } } public class AllowAnyRedirectService : RedirectService { public AllowAnyRedirectService(ILogger logger, ISanitizer sanitizer) : base(logger, sanitizer) { AddRedirectRule(RedirectRule.AllowAny); } } public class RedirectService : IRedirectService { public RedirectService(ILogger logger, ISanitizer sanitizer) { _logger = logger; _sanitizer = sanitizer; } private readonly List _rules = new List(); private readonly ILogger _logger; private readonly ISanitizer _sanitizer; public IRedirectService AddRedirectRule(RedirectRule rule) { _rules.Add(rule); return this; } public IRedirectService ClearRules() { _rules.Clear(); return this; } public IRedirectService AddRule(RedirectRule rule) { _rules.Add(rule); return this; } public IRedirectService RemoveRule(RedirectRule rule) { _rules.Remove(rule); return this; } public IRedirectService AddRules(IEnumerable rules) { _rules.AddRange(rules); return this; } public IRedirectService RemoveRules(IEnumerable rules) { foreach (var rule in rules) { RemoveRule(rule); } return this; } public virtual bool IsRedirectAllowed(string redirectUrl) { if (!Uri.TryCreate(redirectUrl, UriKind.RelativeOrAbsolute, out var uri)) { _logger.LogWarning("Invalid URL: {redirectUrl}", redirectUrl.SanitizeForLog()); return false; } // If the URL is relative, assume it's allowed, or handle according to your policy if (!uri.IsAbsoluteUri) { // Handle relative URLs based on your specific requirements. // For example, we might always allow them, check them against a specific set of rules, // or reject them outright. return HandleRelativeUrl(uri); } foreach (var rule in _rules) { if (IsRuleMatch(uri, rule)) { return true; } } return false; } public bool HandleRelativeUrl(Uri uri) { // Implement logic for handling relative URLs on local server. // This is a placeholder as restricting redirect URLs on the local server is not a common scenario. // For now: return true to allow all relative URLs return true; } public bool IsRuleMatch(Uri uri, RedirectRule rule) { return IsSchemeMatch(uri, rule.AllowedScheme) && IsHostMatch(uri, rule.AllowedHost) && IsPortMatch(uri, rule.AllowedPort) && IsPathMatch(uri, rule.AllowedPath) && IsQueryMatch(uri, rule.AllowedQuery) && IsFragmentMatch(uri, rule.AllowedFragment); } public bool IsSchemeMatch(Uri uri, Scheme allowedScheme) { return allowedScheme == Scheme.Any || uri.Scheme.Equals(allowedScheme.Name, StringComparison.OrdinalIgnoreCase); } public bool IsHostMatch(string url, Host allowedHost) { if (string.IsNullOrWhiteSpace(url)) { return false; } if (allowedHost == Host.Any) { return true; } // Check if URL already has a valid scheme; if not, prepend "http://" as a default if (!url.StartsWith("http://", StringComparison.OrdinalIgnoreCase) && !url.StartsWith("https://", StringComparison.OrdinalIgnoreCase)) { url = "http://" + url; } // Attempt to parse the URL into a Uri object if (Uri.TryCreate(url, UriKind.Absolute, out var uri)) { // Delegate to the existing IsHostMatch method that takes a Uri object return IsHostMatch(uri, allowedHost); } else { // Log error or handle invalid URL format as needed return false; } } public bool IsHostMatch(Uri uri, Host allowedHost) { // Handle the case where any host is allowed if (allowedHost == Host.Any) { return true; } string uriHost = uri.Host; string allowedHostValue = allowedHost.Value; // Direct match or wildcard match if (uriHost.Equals(allowedHostValue, StringComparison.OrdinalIgnoreCase) || allowedHostValue == "*") { return true; } // Check for wildcard subdomain match if (allowedHostValue.StartsWith("*.")) { string allowedDomain = allowedHostValue.Substring(2); if (uriHost.EndsWith(allowedDomain, StringComparison.OrdinalIgnoreCase) && // Additional check to ensure we're matching subdomains and not just any part of the host (uriHost.Count(c => c == '.') == allowedDomain.Count(c => c == '.') + 1)) { return true; } } return false; } public bool IsPortMatch(Uri uri, Port allowedPort) { return allowedPort == Port.Any || uri.Port == allowedPort.Value; } public bool IsPathMatch(Uri uri, Path allowedPath) { return allowedPath == Path.Any || uri.AbsolutePath.Equals(allowedPath.Value, StringComparison.OrdinalIgnoreCase); } public bool IsQueryMatch(Uri uri, Query allowedQuery) { return allowedQuery == Query.Any || uri.Query.Equals(allowedQuery.Value, StringComparison.OrdinalIgnoreCase); } public bool IsFragmentMatch(Uri uri, Fragment allowedFragment) { return allowedFragment == Fragment.Any || uri.Fragment.Equals(allowedFragment.Value, StringComparison.OrdinalIgnoreCase); } } public class RedirectUrlValidator { private readonly IRedirectService _redirectService; public RedirectUrlValidator(IRedirectService redirectService) { _redirectService = redirectService; } public bool IsRedirectUrlValid(string redirectUrl) { return _redirectService.IsRedirectAllowed(redirectUrl); } } ================================================ FILE: src/Security/IdentityServer8.Security/RedirectUrlParser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Security; public readonly ref struct RedirectUrlS { private readonly ReadOnlySpan _input; public RedirectUrlS(string input) { _input = input.AsSpan(); } //public RedirectUrlS Parse() //{ // // Default values representing wildcards // var scheme = Scheme.Any; // var host = Host.Any; // var port = Port.Any; // var path = Path.Any; // var query = Query.Any; // var fragment = Fragment.Any; // int offset = 0; // int schemeEnd = _input.IndexOf("://"); // if (schemeEnd != -1) // { // scheme = new Scheme(_input.Slice(0, schemeEnd).ToString()); // offset = schemeEnd + 3; // } // int pathStart = _input.IndexOf('/'); // int portEnd = _input.Slice(0, pathStart != -1 ? pathStart : _input.Length).IndexOf(':'); // if (portEnd != -1) // { // host = new Host(_input.Slice(0, portEnd).ToString()); // port = new Port(int.Parse(_input.Slice(portEnd + 1, pathStart - portEnd - 1).ToString())); // } // else if (pathStart != -1) // { // host = new Host(_input.Slice(0, pathStart).ToString()); // } // else // { // host = new Host(_input.ToString()); // } // if (pathStart != -1) // { // offset = pathStart; // int queryStart = _input.IndexOf('?'); // int fragmentStart = _input.IndexOf('#'); // if (queryStart != -1) // { // path = new Path(_input.Slice(0, queryStart).ToString()); // offset = queryStart + 1; // if (fragmentStart != -1) // { // query = new Query(_input.Slice(0, fragmentStart - queryStart - 1).ToString()); // fragment = new Fragment(_input.Slice(fragmentStart + 1).ToString()); // } // else // { // query = new Query(_input.ToString()); // } // } // else if (fragmentStart != -1) // { // path = new Path(_input.Slice(0, fragmentStart).ToString()); // fragment = new Fragment(_input.Slice(fragmentStart + 1).ToString()); // } // else // { // path = new Path(_input.ToString()); // } // } // return new RedirectUrl // { // Scheme = scheme, // Host = host, // Port = port, // Path = path, // Query = query, // Fragment = fragment // }; //} } public class RedirectUrlParser { public static UrlParts Parse(string redirectUrl) { // extract scheme, host, port, path, query, and fragment from the redirectUrl // and return a RedirectUrl object // expand: example.com to *://**.example.com:*/**?**#* // expand: example.com/path to *://**.example.com:*/path?**#* // expand: example.com/* to *://**.example.com:*/**?**#* // expand: example.com/path/* to *://**.example.com:*/path/*?**#* // expand: example.com/path/** to *://**.example.com:*/path/**?**#* // expand: example.com/path/1/* to *://**.example.com:*/path/1/*?query#fragment // expand: example.com/path/1/** to *://**.example.com:*/path/1/**?query#fragment // expand: example.com/path/** to *://**.example.com:*/path/**?**#* // expand: example.com/path/1/path/2?quru to *://**.example.com:*/path?query#fragment var schemeParts = redirectUrl.Split("://"); var originalScheme = schemeParts.Length == 1 ? "" : schemeParts[0]; var scheme = schemeParts.Length == 1 ? Scheme.Any : Scheme.Parse(schemeParts[0]); var tail = redirectUrl.Substring(schemeParts.Length == 1 ? 0 : schemeParts[0].Length + 3); var idx = tail.IndexOf("/"); var hostAndPort = tail.Substring(0, idx); var portDelimiter = hostAndPort.IndexOf(":"); var host = portDelimiter > 0 ? hostAndPort.Substring(0, portDelimiter) : hostAndPort; var port = portDelimiter > 0 ? hostAndPort.Substring(portDelimiter + 1) : ""; int portNumber = 0; if(int.TryParse(port, out var parsedPort)) { portNumber = parsedPort; } else { //TODO: Modify port to accept wildcards, multiple ports, and ranges //port = ""; } var domainParts = host.Split("."); var absolutePath = idx > -1 ? tail.Substring(idx) : ""; var pathParts = absolutePath.Split("?"); var path = pathParts[0]; var query = pathParts.Length == 1 ? "" : pathParts[1]; var queryParts = query.Split("#"); var queryString = queryParts[0]; var queryStringParts = queryString.Split("&"); var fragment = queryParts.Length == 1 ? "" : queryParts[1]; var fragmentParts = fragment.Split("#"); return new UrlParts { Scheme = scheme, Host = Host.Create(host), Port = Port.Create(portNumber), Path = Path.Create(path), Query = Query.Create(queryString), Fragment = Fragment.Create(fragment) }; } } public class UrlParts { public UrlParts() { Scheme= Scheme.Any; Host = Host.Any; Port = Port.Any; Path = Path.Any; Query = Query.Any; Fragment = Fragment.Any; } public UrlParts(Scheme scheme, Host host, Port port, Path path, Query query, Fragment fragment) { Scheme = scheme; Host = host; Port = port; Path = path; Query = query; Fragment = fragment; } public Scheme Scheme { get; set; } public Host Host { get; set; } public Port Port { get; set; } public Path Path { get; set; } public Query Query { get; set; } public Fragment Fragment { get; set; } } ================================================ FILE: src/Security/IdentityServer8.Security/RedirectUrlServiceExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; public static class RedirectUrlServiceExtensions { public static bool IsAllowedRedirect(this string redirectUrl) { return Ioc.RedirectService.IsRedirectAllowed(redirectUrl); } public static bool IsAllowedRedirect(this Uri uri) { return Ioc.RedirectService.IsRedirectAllowed(uri.ToString()); } public static void RedirectIfAllowed(this HttpResponse response, string url) { if (IsAllowedRedirect(url)) response.Redirect(url.SanitizeForRedirect()); else SetRedirectNotAllowed(response); } public static void SetRedirectNotAllowed(this HttpResponse response) { response.StatusCode = (int) HttpStatusCode.Forbidden; } public static void SetRedirectNotAllowed(this HttpContext ctx) { ctx.Response.SetRedirectNotAllowed(); } } ================================================ FILE: src/Security/IdentityServer8.Security/Sanitizer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Security; public enum SanitizerType { Unknown = 0, HtmlSanitizer, XmlSanitizer, JsonSanitizer, UrlSanitizer, CssSanitizer, ScriptSanitizer, StyleSanitizer, SqlSanitizer, LogSanitizer } public enum SanitizerMode { Debug, Clean, Mask, Full } public interface IInputSanitizer { public string? Sanitize(string? input, SanitizerMode mode = SanitizerMode.Clean); } public interface ISanitizerFactory { TSanitizer Create() where TSanitizer : IInputSanitizer; IInputSanitizer Create(SanitizerType type); } public interface IHtmlSanitizer : IInputSanitizer { } public interface IXmlSanitizer : IInputSanitizer { } public interface IJsonSanitizer : IInputSanitizer { } public interface IUrlSanitizer : IInputSanitizer { } public interface ICssSanitizer : IInputSanitizer { } public interface IScriptSanitizer : IInputSanitizer { } public interface IStyleSanitizer : IInputSanitizer { } public interface ISqlSanitizer : IInputSanitizer { } public interface ILogSanitizer : IInputSanitizer { } public interface ISanitizerService { string? Sanitize(string? input, SanitizerType type, SanitizerMode mode); } public abstract class SanitizerServiceBase : ISanitizerService { public abstract string? Sanitize(string? input, SanitizerType type, SanitizerMode mode); } public class SanitizerService : SanitizerServiceBase { private readonly ISanitizerFactory _sanitizerFactory; public SanitizerService(ISanitizerFactory sanitizerFactory) { _sanitizerFactory = sanitizerFactory; } public override string? Sanitize(string? input, SanitizerType type, SanitizerMode mode) { var sanitizer = _sanitizerFactory.Create(type); return sanitizer.Sanitize(input, mode); } } public class SanitizerBase : IInputSanitizer { private Func _sanitize; public SanitizerBase() : this(HttpUtility.HtmlEncode) { } public SanitizerBase(Func sanitizer) { _sanitize = sanitizer; } public virtual string? Sanitize(string? input, SanitizerMode mode = SanitizerMode.Debug) { switch (mode) { case SanitizerMode.Debug: return input; case SanitizerMode.Clean: return Clean(input); case SanitizerMode.Mask: case SanitizerMode.Full: var result = _sanitize(input) ?? ""; switch (mode) { case SanitizerMode.Mask: return Mask(result); case SanitizerMode.Full: return result; default: throw new NotImplementedException(); } default: throw new NotImplementedException(); } } public string? Mask(string? input, int unmaskedChars = 4, bool unmaskFirst = false) { if (string.IsNullOrEmpty(input)) return input; if (unmaskedChars == 0) { return "********"; } input = Clean(input); if (input.Length <= unmaskedChars) { return new string('*', input.Length); } else if (unmaskFirst) { return input.Substring(0, unmaskedChars) + new string('*', input.Length - unmaskedChars); } else { return new string('*', input.Length - unmaskedChars) + input.Substring(input.Length - unmaskedChars); } } public string Clean(string? input) { input = input ?? string.Empty; input = input.Replace("\r", " ").Replace("\n", " "); var idx = input.IndexOf(" "); while (idx > -1) { input = input.Replace(" ", " "); idx = input.IndexOf(" "); } input = _sanitize(input) ?? ""; //unescape ' and " and space input = input.Replace("'", "'"); input = input.Replace(""", "\""); input = input.Replace("", " "); input = input.Replace("'", "'"); input = input.Replace(""", "\""); input = input.Replace(" ", " "); return input.Trim() ?? ""; } } public class HtmlSanitizer : SanitizerBase, IHtmlSanitizer { public HtmlSanitizer() : base() { } } public class XmlSanitizer : SanitizerBase, IXmlSanitizer { public XmlSanitizer() : base(HttpUtility.HtmlEncode) { } } public class JsonSanitizer : SanitizerBase, IJsonSanitizer { public JsonSanitizer() : base(HttpUtility.JavaScriptStringEncode) { } } public class UrlSanitizer : SanitizerBase, IUrlSanitizer { public UrlSanitizer() : base(x => Uri.EscapeUriString(x?.ToString() ?? "")) { } } public class CssSanitizer : SanitizerBase, ICssSanitizer { public CssSanitizer() : base() { } } public class ScriptSanitizer : SanitizerBase, IScriptSanitizer { public ScriptSanitizer() : base(x => Uri.EscapeDataString(x?.ToString() ?? "")) { } } public class StyleSanitizer : SanitizerBase, IStyleSanitizer { public StyleSanitizer() : base() { } } public class SqlSanitizer : SanitizerBase, ISqlSanitizer { public SqlSanitizer() : base() { } } public class LogSanitizer : SanitizerBase, ILogSanitizer { public LogSanitizer() : base(HttpUtility.HtmlEncode) { } public override string? Sanitize(string? input, SanitizerMode mode) { if (input is null) return input; switch (mode) { case SanitizerMode.Debug: return base.Mask(input, input.Length); case SanitizerMode.Clean: return base.Clean(input); case SanitizerMode.Mask: return base.Mask(input); case SanitizerMode.Full: return base.Mask(input, 0); default: throw new NotImplementedException(); } } } public class SanitizerFactory : ISanitizerFactory { public TInputSanitizer Create() where TInputSanitizer : IInputSanitizer { var type = Enum.Parse(typeof(TInputSanitizer).Name.Substring(1)); return (TInputSanitizer) Create(type); } public IInputSanitizer Create(SanitizerType type) { switch (type) { case SanitizerType.HtmlSanitizer: return new HtmlSanitizer(); case SanitizerType.XmlSanitizer: return new XmlSanitizer(); case SanitizerType.JsonSanitizer: return new JsonSanitizer(); case SanitizerType.UrlSanitizer: return new UrlSanitizer(); case SanitizerType.CssSanitizer: return new CssSanitizer(); case SanitizerType.ScriptSanitizer: return new ScriptSanitizer(); case SanitizerType.StyleSanitizer: return new StyleSanitizer(); case SanitizerType.SqlSanitizer: return new SqlSanitizer(); case SanitizerType.LogSanitizer: return new LogSanitizer(); default: throw new NotImplementedException(); } } } public interface ISanitizer { public IHtmlSanitizer Html { get; } public IXmlSanitizer Xml { get; } public IJsonSanitizer Json { get; } public IUrlSanitizer Url { get; } public ICssSanitizer Css { get; } public IScriptSanitizer Script { get; } public IStyleSanitizer Style { get; } public ISqlSanitizer Sql { get; } public ILogSanitizer Log { get; } } public class Sanitizer : ISanitizer { public Sanitizer(ISanitizerFactory sanitizerFactory) { Html = sanitizerFactory.Create(); Xml = sanitizerFactory.Create(); Json = sanitizerFactory.Create(); Url = sanitizerFactory.Create(); Css = sanitizerFactory.Create(); Script = sanitizerFactory.Create(); Style = sanitizerFactory.Create(); Sql = sanitizerFactory.Create(); Log = sanitizerFactory.Create(); } public IHtmlSanitizer Html { get; } public IXmlSanitizer Xml { get; } public IJsonSanitizer Json { get; } public IUrlSanitizer Url { get; } public ICssSanitizer Css { get; } public IScriptSanitizer Script { get; } public IStyleSanitizer Style { get; } public ISqlSanitizer Sql { get; } public ILogSanitizer Log { get; } } ================================================ FILE: src/Security/IdentityServer8.Security/SanitizerServiceExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace Microsoft.Extensions.DependencyInjection; public static class SanitizerServiceExtensions { public static IServiceCollection AddSanitizers(this IServiceCollection services) { var factory = new SanitizerFactory(); services.AddSingleton(factory); services.AddSingleton(factory); services.AddSingleton(); services.AddSingleton(); var props = typeof(ISanitizer).GetProperties(); foreach (var prop in props) { var type = Enum.Parse(prop.PropertyType.Name.Substring(1)); var sanitizer = factory.Create(type); services.AddSingleton(prop.PropertyType, sanitizer); services.AddSingleton(sanitizer); } return services; } public static string? SanitizeForLog(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Log.Sanitize(input?.ToString(), mode); } public static string? SanitizeForHtml(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Html.Sanitize(input?.ToString(), mode); } public static string? SanitizeForXml(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Xml.Sanitize(input?.ToString(), mode); } public static string? SanitizeForJson(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Json.Sanitize(input?.ToString(), mode); } public static string SanitizeForRedirect(this object? input, SanitizerMode mode = SanitizerMode.Clean) { var rawUrl = input?.ToString() ?? ""; if (string.IsNullOrEmpty(rawUrl)) return rawUrl; else { if (Uri.TryCreate(rawUrl, UriKind.RelativeOrAbsolute, out var uri)) { var parsedUrl = uri.ToString(); if (parsedUrl == rawUrl.ToString()) return parsedUrl; else { return uri.ToString().SanitizeForLog() ?? ""; } } else { throw new ArgumentException("Invalid URL", nameof(input)); } } } public static string? SanitizeForUrl(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Url.Sanitize(input?.ToString(), mode); } public static string? SanitizeForCss(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Css.Sanitize(input?.ToString(), mode); } public static string? SanitizeForScript(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Script.Sanitize(input?.ToString(), mode); } public static string? SanitizeForStyle(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Style.Sanitize(input?.ToString(), mode); } public static string? SanitizeForSql(this object? input, SanitizerMode mode = SanitizerMode.Clean) { return Ioc.Sanitizer.Sql.Sanitize(input?.ToString(), mode); } public static string? SantizeForRedirect(this object? input, SanitizerMode mode = SanitizerMode.Clean) { var decoded = Uri.UnescapeDataString(input?.ToString() ?? ""); decoded.SanitizeForHtml(); var escaped = Uri.EscapeDataString(decoded); return escaped; } } ================================================ FILE: src/Security/test/IdentityServer8.Santizer.Tests/IdentityServer8.Sanitizer.Tests.csproj ================================================ false runtime; build; native; contentfiles; analyzers; buildtransitive all ================================================ FILE: src/Security/test/IdentityServer8.Santizer.Tests/RedirectServiceTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using Microsoft.Extensions.DependencyInjection; using Xunit; namespace IdentityServer8.Security.Tests; public class DependencyInjection { static DependencyInjection() { var provider = new ServiceCollection() .AddLogging() .AddAllowAnyRedirectService() .AddSingleton() .AddSanitizers() .BuildServiceProvider(); ServiceProvider = provider; } public static ServiceProvider ServiceProvider { get; } } public class AllowAnyTests { private readonly RedirectService _redirectService; public AllowAnyTests() { var redirectService = DependencyInjection.ServiceProvider.GetRequiredService(); _redirectService = redirectService; } [Theory] [InlineData("http://example.com", true)] [InlineData("http://a.example.com", true)] [InlineData("http://a.b.example.com", true)] [InlineData("https://example.com", true)] [InlineData("https://a.example.com", true)] [InlineData("https://a.b.example.com", true)] [InlineData("http://localhost", true)] [InlineData("https://localhost", true)] public void AllowAnyShouldReturnTrue(string uriString, bool expectedResult) { var result = _redirectService.IsRedirectAllowed(uriString); Assert.Equal(expectedResult, result); } } public class RedirectServiceTests { private readonly RedirectService _redirectService; public RedirectServiceTests() { var redirectService = DependencyInjection.ServiceProvider.GetService(); _redirectService = redirectService as RedirectService; } [Theory] [InlineData("http://example.com", "example.com", true)] [InlineData("http://example.com", "*", true)] [InlineData("http://example.com", "another.com", false)] [InlineData("example.com", "example.com", true)] [InlineData("example.com", "*", true)] [InlineData("example.com", "another.com", false)] [InlineData("*.example.com", "example.com", false)] [InlineData("*.example.com", "*", true)] [InlineData("*.example.com", "another.com", false)] public void IsHostMatch_ShouldCorrectlyMatchHost(string uriString, string allowedHostName, bool expectedResult) { var allowedHost = allowedHostName == "*" ? Host.Any : Host.Create(allowedHostName); // Assuming IsHostMatch is made internal for testing and accessible here var result = _redirectService.IsHostMatch(uriString, allowedHost); Assert.Equal(expectedResult, result); } [Fact()] public void RedirectServiceTest() { } [Fact()] public void AddARedirectRuleTest() { } [Fact()] public void IsRedirectAllowedTest() { } } ================================================ FILE: src/Security/test/IdentityServer8.Santizer.Tests/Services/SanitizerTests.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ using FluentAssertions; using Microsoft.Extensions.DependencyInjection; using System.Diagnostics; using System.Text.Json; using Xunit; namespace IdentityServer8.Security.Tests; public class SanitizerTests { public SanitizerTests() { } [Fact] public void SanitizerFactory() { var factory = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(factory); } [Fact] public void SanitizerService() { var service = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(service); } [Fact] public void Sanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); } [Fact] public void HtmlSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = "
    "; var output = sanitizer.Sanitize(input); var expected = "<div><script>alert('xss')</script></div>"; Validate(expected, output); } [Fact] public void XmlSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = "
    "; var output = sanitizer.Sanitize(input); var expected = "<div><script>alert('xss')</script></div>"; Validate(expected, output); } [Fact] public void JsonSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = JsonSerializer.Serialize(new { test = "test", value = "
    " }); var output = sanitizer.Sanitize(input); var expected = @"{\""test\"":\""test\"",\""value\"":\""\\u003Cdiv\\u003E\\u003Cscript\\u003Ealert(\\u0027xss\\u0027)\\u003C/script\\u003E\\u003C/div\\u003E\""}"; Validate(expected, output); } [Fact] public void UrlSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = "http://test.com?test=
    "; var output = sanitizer.Sanitize(input); var expected = "http://test.com?test=%3Cdiv%3E%3Cscript%3Ealert('xss')%3C/script%3E%3C/div%3E"; Validate(expected, output); } [Fact] public void CssSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = "div { background-url('') }"; var output = sanitizer.Sanitize(input); var expected = "div { background-url('<script>alert('xss')</script>') }"; Validate(expected, output); } [Fact] public void ScriptSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = JsonSerializer.Serialize(new { test = "test", value = "
    " }); var output = sanitizer.Sanitize(input); var expected = @"{\""test\"":\""test\"",\""value\"":\""\\u003Cdiv\\u003E\\u003Cscript\\u003Ealert(\\u0027xss\\u0027)\\u003C/script\\u003E\\u003C/div\\u003E\""}"; Validate(expected, output); } [Fact] public void StyleSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = "div { background-url('') }"; var output = sanitizer.Sanitize(input); var expected = "div { background-url('<script>alert('xss')</script>') }"; Validate(expected, output); } [Fact] public void SqlSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = "update table set value='' where 1=1;"; var output = sanitizer.Sanitize(input); var expected = "update table set value='<script>alert('xss')</script>' where 1=1;"; Validate(expected, output); } [Fact] public void LogSanitizer() { var sanitizer = Ioc.ServiceProvider.GetRequiredService(); Assert.NotNull(sanitizer); var input = @"log poisoning test "; var output = sanitizer.Sanitize(input); var expected = "log poisoning test <script>alert('xss')</script>"; Validate(expected, output); output = sanitizer.Sanitize("mypassword", SanitizerMode.Mask); expected = "******word"; Validate(expected, output); output = sanitizer.Sanitize("mypassword", SanitizerMode.Full); expected = "********"; Validate(expected, output); } void Validate(string expected, string output) { Console.WriteLine("Expected: " + expected); Console.WriteLine("Output: " + output); Debug.WriteLine("Expected: " + expected); Debug.WriteLine("Output: " + output); output.Should().Be(expected); } private void Log(string expected, string output) { Console.WriteLine("Expected: " + expected); Console.WriteLine("Output: " + output); Debug.WriteLine("Expected: " + expected); Debug.WriteLine("Output: " + output); } } ================================================ FILE: src/Storage/Directory.Build.props ================================================ ================================================ FILE: src/Storage/IdentityServer8.Storage.sln ================================================ Microsoft Visual Studio Solution File, Format Version 12.00 # Visual Studio 15 VisualStudioVersion = 15.0.26124.0 MinimumVisualStudioVersion = 15.0.26124.0 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{2EEC146C-3C96-4871-BBAF-8341719BD533}" EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IdentityServer8.Storage", "src\IdentityServer8.Storage.csproj", "{A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 Debug|x86 = Debug|x86 Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 Release|x86 = Release|x86 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Debug|Any CPU.Build.0 = Debug|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Debug|x64.ActiveCfg = Debug|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Debug|x64.Build.0 = Debug|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Debug|x86.ActiveCfg = Debug|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Debug|x86.Build.0 = Debug|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Release|Any CPU.ActiveCfg = Release|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Release|Any CPU.Build.0 = Release|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Release|x64.ActiveCfg = Release|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Release|x64.Build.0 = Release|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Release|x86.ActiveCfg = Release|Any CPU {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7}.Release|x86.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution {A5E2B0DD-9C88-42B5-BCEC-7ED701504FF7} = {2EEC146C-3C96-4871-BBAF-8341719BD533} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {AFF22F24-FEA5-4A1D-AFE6-942789486D59} EndGlobalSection EndGlobal ================================================ FILE: src/Storage/README.md ================================================ # IdentityServer8.Storage IdentityServer8.Storage contains all the models and storage interfaces for IdentityServer 4 configuration data. ================================================ FILE: src/Storage/build/Program.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace build { partial class Program { private const string Prefix = "Storage"; } } ================================================ FILE: src/Storage/build/build.csproj ================================================ Exe ================================================ FILE: src/Storage/build.cmd ================================================ @echo off dotnet run --project build -- %* ================================================ FILE: src/Storage/build.ps1 ================================================ $ErrorActionPreference = "Stop"; dotnet run --project build -- $args ================================================ FILE: src/Storage/build.sh ================================================ #!/usr/bin/env bash set -euo pipefail dotnet run --project build -- "$@" ================================================ FILE: src/Storage/src/Constants.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8; internal static class Constants { public const string IdentityServerName = "IdentityServer8"; public const string IdentityServerAuthenticationType = IdentityServerName; } ================================================ FILE: src/Storage/src/Extensions/IEnumerableExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Extensions; internal static class IEnumerableExtensions { [DebuggerStepThrough] public static bool IsNullOrEmpty(this IEnumerable list) { if (list == null) { return true; } if (!list.Any()) { return true; } return false; } } ================================================ FILE: src/Storage/src/Extensions/PersistedGrantFilterExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; /// /// Extensions for PersistedGrantFilter. /// public static class PersistedGrantFilterExtensions { /// /// Validates the PersistedGrantFilter and throws if invalid. /// /// public static void Validate(this PersistedGrantFilter filter) { if (filter == null) throw new ArgumentNullException(nameof(filter)); if (String.IsNullOrWhiteSpace(filter.ClientId) && String.IsNullOrWhiteSpace(filter.SessionId) && String.IsNullOrWhiteSpace(filter.SubjectId) && String.IsNullOrWhiteSpace(filter.Type)) { throw new ArgumentException("No filter values set.", nameof(filter)); } } } ================================================ FILE: src/Storage/src/Extensions/StringsExtensions.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Extensions; internal static class StringExtensions { [DebuggerStepThrough] public static bool IsMissing(this string value) { return string.IsNullOrWhiteSpace(value); } [DebuggerStepThrough] public static bool IsPresent(this string value) { return !string.IsNullOrWhiteSpace(value); } } ================================================ FILE: src/Storage/src/GlobalUsings.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ global using IdentityModel; global using IdentityServer8.Extensions; global using IdentityServer8.Models; global using IdentityServer8.Stores; global using Newtonsoft.Json; global using Newtonsoft.Json.Serialization; global using System.Collections; global using System.Diagnostics; global using System.Security.Claims; ================================================ FILE: src/Storage/src/IdentityServer8.Storage.csproj ================================================ Storage interfaces and models for IdentityServer8 true True True True True \ ================================================ FILE: src/Storage/src/IdentityServerConstants.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8; internal static class IdentityServerConstants { public static class ProtocolTypes { public const string OpenIdConnect = "oidc"; } public static class SecretTypes { public const string SharedSecret = "SharedSecret"; } } ================================================ FILE: src/Storage/src/IdentityServerUser.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8; /// /// Model properties of an IdentityServer user /// internal class IdentityServerUser { /// /// Subject ID (mandatory) /// public string SubjectId { get; } /// /// Display name (optional) /// public string DisplayName { get; set; } /// /// Identity provider (optional) /// public string IdentityProvider { get; set; } /// /// Authentication methods /// public ICollection AuthenticationMethods { get; set; } = new HashSet(); /// /// Authentication time /// public DateTime? AuthenticationTime { get; set; } /// /// Additional claims /// public ICollection AdditionalClaims { get; set; } = new HashSet(new ClaimComparer()); /// /// Initializes a user identity /// /// The subject ID public IdentityServerUser(string subjectId) { if (subjectId.IsMissing()) throw new ArgumentException("SubjectId is mandatory", nameof(subjectId)); SubjectId = subjectId; } /// /// Creates an IdentityServer claims principal /// /// /// public ClaimsPrincipal CreatePrincipal() { if (SubjectId.IsMissing()) throw new ArgumentException("SubjectId is mandatory", nameof(SubjectId)); var claims = new List { new Claim(JwtClaimTypes.Subject, SubjectId) }; if (DisplayName.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.Name, DisplayName)); } if (IdentityProvider.IsPresent()) { claims.Add(new Claim(JwtClaimTypes.IdentityProvider, IdentityProvider)); } if (AuthenticationTime.HasValue) { claims.Add(new Claim(JwtClaimTypes.AuthenticationTime, new DateTimeOffset(AuthenticationTime.Value).ToUnixTimeSeconds().ToString())); } if (AuthenticationMethods.Any()) { foreach (var amr in AuthenticationMethods) { claims.Add(new Claim(JwtClaimTypes.AuthenticationMethod, amr)); } } claims.AddRange(AdditionalClaims); var id = new ClaimsIdentity(claims.Distinct(new ClaimComparer()), Constants.IdentityServerAuthenticationType, JwtClaimTypes.Name, JwtClaimTypes.Role); return new ClaimsPrincipal(id); } } ================================================ FILE: src/Storage/src/Models/ApiResource.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models a web API resource. /// [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] public class ApiResource : Resource { private string DebuggerDisplay => Name ?? $"{{{typeof(ApiResource)}}}"; /// /// Initializes a new instance of the class. /// public ApiResource() { } /// /// Initializes a new instance of the class. /// /// The name. public ApiResource(string name) : this(name, name, null) { } /// /// Initializes a new instance of the class. /// /// The name. /// The display name. public ApiResource(string name, string displayName) : this(name, displayName, null) { } /// /// Initializes a new instance of the class. /// /// The name. /// List of associated user claims that should be included when this resource is requested. public ApiResource(string name, IEnumerable userClaims) : this(name, name, userClaims) { } /// /// Initializes a new instance of the class. /// /// The name. /// The display name. /// List of associated user claims that should be included when this resource is requested. /// name public ApiResource(string name, string displayName, IEnumerable userClaims) { if (name.IsMissing()) throw new ArgumentNullException(nameof(name)); Name = name; DisplayName = displayName; if (!userClaims.IsNullOrEmpty()) { foreach (var type in userClaims) { UserClaims.Add(type); } } } /// /// The API secret is used for the introspection endpoint. The API can authenticate with introspection using the API name and secret. /// public ICollection ApiSecrets { get; set; } = new HashSet(); /// /// Models the scopes this API resource allows. /// public ICollection Scopes { get; set; } = new HashSet(); /// /// Signing algorithm for access token. If empty, will use the server default signing algorithm. /// public ICollection AllowedAccessTokenSigningAlgorithms { get; set; } = new HashSet(); } ================================================ FILE: src/Storage/src/Models/ApiScope.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models access to an API scope /// [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] public class ApiScope : Resource { private string DebuggerDisplay => Name ?? $"{{{typeof(ApiScope)}}}"; /// /// Initializes a new instance of the class. /// public ApiScope() { } /// /// Initializes a new instance of the class. /// /// The name. public ApiScope(string name) : this(name, name, null) { } /// /// Initializes a new instance of the class. /// /// The name. /// The display name. public ApiScope(string name, string displayName) : this(name, displayName, null) { } /// /// Initializes a new instance of the class. /// /// The name. /// List of associated user claims that should be included when this resource is requested. public ApiScope(string name, IEnumerable userClaims) : this(name, name, userClaims) { } /// /// Initializes a new instance of the class. /// /// The name. /// The display name. /// List of associated user claims that should be included when this resource is requested. /// name public ApiScope(string name, string displayName, IEnumerable userClaims) { if (name.IsMissing()) throw new ArgumentNullException(nameof(name)); Name = name; DisplayName = displayName; if (!userClaims.IsNullOrEmpty()) { foreach (var type in userClaims) { UserClaims.Add(type); } } } /// /// Specifies whether the user can de-select the scope on the consent screen. Defaults to false. /// public bool Required { get; set; } = false; /// /// Specifies whether the consent screen will emphasize this scope. Use this setting for sensitive or important scopes. Defaults to false. /// public bool Emphasize { get; set; } = false; } ================================================ FILE: src/Storage/src/Models/AuthorizationCode.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models an authorization code. /// public class AuthorizationCode { /// /// Gets or sets the creation time. /// /// /// The creation time. /// public DateTime CreationTime { get; set; } /// /// Gets or sets the life time in seconds. /// /// /// The life time. /// public int Lifetime { get; set; } /// /// Gets or sets the ID of the client. /// /// /// The ID of the client. /// public string ClientId { get; set; } /// /// Gets or sets the subject. /// /// /// The subject. /// public ClaimsPrincipal Subject { get; set; } /// /// Gets or sets a value indicating whether this code is an OpenID Connect code. /// /// /// true if this instance is open identifier; otherwise, false. /// public bool IsOpenId { get; set; } /// /// Gets or sets the requested scopes. /// /// /// The requested scopes. /// // todo: brock, change to parsed scopes public IEnumerable RequestedScopes { get; set; } /// /// Gets or sets the redirect URI. /// /// /// The redirect URI. /// public string RedirectUri { get; set; } /// /// Gets or sets the nonce. /// /// /// The nonce. /// public string Nonce { get; set; } /// /// Gets or sets the hashed state (to output s_hash claim). /// /// /// The hashed state. /// public string StateHash { get; set; } /// /// Gets or sets a value indicating whether consent was shown. /// /// /// true if consent was shown; otherwise, false. /// public bool WasConsentShown { get; set; } /// /// Gets or sets the session identifier. /// /// /// The session identifier. /// public string SessionId { get; set; } /// /// Gets or sets the code challenge. /// /// /// The code challenge. /// public string CodeChallenge { get; set; } /// /// Gets or sets the code challenge method. /// /// /// The code challenge method /// public string CodeChallengeMethod { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } /// /// Gets or sets properties /// /// /// The properties /// public IDictionary Properties { get; set; } = new Dictionary(); } ================================================ FILE: src/Storage/src/Models/Client.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models an OpenID Connect or OAuth2 client /// [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] public class Client { // setting grant types should be atomic private ICollection _allowedGrantTypes = new GrantTypeValidatingHashSet(); private string DebuggerDisplay => ClientId ?? $"{{{typeof(Client)}}}"; /// /// Specifies if client is enabled (defaults to true) /// public bool Enabled { get; set; } = true; /// /// Unique ID of the client /// public string ClientId { get; set; } /// /// Gets or sets the protocol type. /// /// /// The protocol type. /// public string ProtocolType { get; set; } = IdentityServerConstants.ProtocolTypes.OpenIdConnect; /// /// Client secrets - only relevant for flows that require a secret /// public ICollection ClientSecrets { get; set; } = new HashSet(); /// /// If set to false, no client secret is needed to request tokens at the token endpoint (defaults to true) /// public bool RequireClientSecret { get; set; } = true; /// /// Client display name (used for logging and consent screen) /// public string ClientName { get; set; } /// /// Description of the client. /// public string Description { get; set; } /// /// URI to further information about client (used on consent screen) /// public string ClientUri { get; set; } /// /// URI to client logo (used on consent screen) /// public string LogoUri { get; set; } /// /// Specifies whether a consent screen is required (defaults to false) /// public bool RequireConsent { get; set; } = false; /// /// Specifies whether user can choose to store consent decisions (defaults to true) /// public bool AllowRememberConsent { get; set; } = true; /// /// Specifies the allowed grant types (legal combinations of AuthorizationCode, Implicit, Hybrid, ResourceOwner, ClientCredentials). /// public ICollection AllowedGrantTypes { get { return _allowedGrantTypes; } set { ValidateGrantTypes(value); _allowedGrantTypes = new GrantTypeValidatingHashSet(value); } } /// /// Specifies whether a proof key is required for authorization code based token requests (defaults to true). /// public bool RequirePkce { get; set; } = true; /// /// Specifies whether a proof key can be sent using plain method (not recommended and defaults to false.) /// public bool AllowPlainTextPkce { get; set; } = false; /// /// Specifies whether the client must use a request object on authorize requests (defaults to false.) /// public bool RequireRequestObject { get; set; } = false; /// /// Controls whether access tokens are transmitted via the browser for this client (defaults to false). /// This can prevent accidental leakage of access tokens when multiple response types are allowed. /// /// /// true if access tokens can be transmitted via the browser; otherwise, false. /// public bool AllowAccessTokensViaBrowser { get; set; } = false; /// /// Specifies allowed URIs to return tokens or authorization codes to /// public ICollection RedirectUris { get; set; } = new HashSet(); /// /// Specifies allowed URIs to redirect to after logout /// public ICollection PostLogoutRedirectUris { get; set; } = new HashSet(); /// /// Specifies logout URI at client for HTTP front-channel based logout. /// public string FrontChannelLogoutUri { get; set; } /// /// Specifies if the user's session id should be sent to the FrontChannelLogoutUri. Defaults to true. /// public bool FrontChannelLogoutSessionRequired { get; set; } = true; /// /// Specifies logout URI at client for HTTP back-channel based logout. /// public string BackChannelLogoutUri { get; set; } /// /// Specifies if the user's session id should be sent to the BackChannelLogoutUri. Defaults to true. /// public bool BackChannelLogoutSessionRequired { get; set; } = true; /// /// Gets or sets a value indicating whether [allow offline access]. Defaults to false. /// public bool AllowOfflineAccess { get; set; } = false; /// /// Specifies the api scopes that the client is allowed to request. If empty, the client can't access any scope /// public ICollection AllowedScopes { get; set; } = new HashSet(); /// /// When requesting both an id token and access token, should the user claims always be added to the id token instead of requiring the client to use the userinfo endpoint. /// Defaults to false. /// public bool AlwaysIncludeUserClaimsInIdToken { get; set; } = false; /// /// Lifetime of identity token in seconds (defaults to 300 seconds / 5 minutes) /// public int IdentityTokenLifetime { get; set; } = 300; /// /// Signing algorithm for identity token. If empty, will use the server default signing algorithm. /// public ICollection AllowedIdentityTokenSigningAlgorithms { get; set; } = new HashSet(); /// /// Lifetime of access token in seconds (defaults to 3600 seconds / 1 hour) /// public int AccessTokenLifetime { get; set; } = 3600; /// /// Lifetime of authorization code in seconds (defaults to 300 seconds / 5 minutes) /// public int AuthorizationCodeLifetime { get; set; } = 300; /// /// Maximum lifetime of a refresh token in seconds. Defaults to 2592000 seconds / 30 days /// public int AbsoluteRefreshTokenLifetime { get; set; } = 2592000; /// /// Sliding lifetime of a refresh token in seconds. Defaults to 1296000 seconds / 15 days /// public int SlidingRefreshTokenLifetime { get; set; } = 1296000; /// /// Lifetime of a user consent in seconds. Defaults to null (no expiration) /// public int? ConsentLifetime { get; set; } = null; /// /// ReUse: the refresh token handle will stay the same when refreshing tokens /// OneTime: the refresh token handle will be updated when refreshing tokens /// public TokenUsage RefreshTokenUsage { get; set; } = TokenUsage.OneTimeOnly; /// /// Gets or sets a value indicating whether the access token (and its claims) should be updated on a refresh token request. /// Defaults to false. /// /// /// true if the token should be updated; otherwise, false. /// public bool UpdateAccessTokenClaimsOnRefresh { get; set; } = false; /// /// Absolute: the refresh token will expire on a fixed point in time (specified by the AbsoluteRefreshTokenLifetime) /// Sliding: when refreshing the token, the lifetime of the refresh token will be renewed (by the amount specified in SlidingRefreshTokenLifetime). The lifetime will not exceed AbsoluteRefreshTokenLifetime. /// public TokenExpiration RefreshTokenExpiration { get; set; } = TokenExpiration.Absolute; /// /// Specifies whether the access token is a reference token or a self contained JWT token (defaults to Jwt). /// public AccessTokenType AccessTokenType { get; set; } = AccessTokenType.Jwt; /// /// Gets or sets a value indicating whether the local login is allowed for this client. Defaults to true. /// /// /// true if local logins are enabled; otherwise, false. /// public bool EnableLocalLogin { get; set; } = true; /// /// Specifies which external IdPs can be used with this client (if list is empty all IdPs are allowed). Defaults to empty. /// public ICollection IdentityProviderRestrictions { get; set; } = new HashSet(); /// /// Gets or sets a value indicating whether JWT access tokens should include an identifier. Defaults to true. /// /// /// true to add an id; otherwise, false. /// public bool IncludeJwtId { get; set; } = true; /// /// Allows settings claims for the client (will be included in the access token). /// /// /// The claims. /// public ICollection Claims { get; set; } = new HashSet(); /// /// Gets or sets a value indicating whether client claims should be always included in the access tokens - or only for client credentials flow. /// Defaults to false /// /// /// true if claims should always be sent; otherwise, false. /// public bool AlwaysSendClientClaims { get; set; } = false; /// /// Gets or sets a value to prefix it on client claim types. Defaults to client_. /// /// /// Any non empty string if claims should be prefixed with the value; otherwise, null. /// public string ClientClaimsPrefix { get; set; } = "client_"; /// /// Gets or sets a salt value used in pair-wise subjectId generation for users of this client. /// public string PairWiseSubjectSalt { get; set; } /// /// The maximum duration (in seconds) since the last time the user authenticated. /// public int? UserSsoLifetime { get; set; } /// /// Gets or sets the type of the device flow user code. /// /// /// The type of the device flow user code. /// public string UserCodeType { get; set; } /// /// Gets or sets the device code lifetime. /// /// /// The device code lifetime. /// public int DeviceCodeLifetime { get; set; } = 300; /// /// Gets or sets the allowed CORS origins for JavaScript clients. /// /// /// The allowed CORS origins. /// public ICollection AllowedCorsOrigins { get; set; } = new HashSet(); /// /// Gets or sets the custom properties for the client. /// /// /// The properties. /// public IDictionary Properties { get; set; } = new Dictionary(); /// /// Validates the grant types. /// /// The grant types. /// /// Grant types list is empty /// or /// Grant types cannot contain spaces /// or /// Grant types list contains duplicate values /// public static void ValidateGrantTypes(IEnumerable grantTypes) { if (grantTypes == null) { throw new ArgumentNullException(nameof(grantTypes)); } // spaces are not allowed in grant types foreach (var type in grantTypes) { if (type.Contains(' ')) { throw new InvalidOperationException("Grant types cannot contain spaces"); } } // single grant type, seems to be fine if (grantTypes.Count() == 1) return; // don't allow duplicate grant types if (grantTypes.Count() != grantTypes.Distinct().Count()) { throw new InvalidOperationException("Grant types list contains duplicate values"); } // would allow response_type downgrade attack from code to token DisallowGrantTypeCombination(GrantType.Implicit, GrantType.AuthorizationCode, grantTypes); DisallowGrantTypeCombination(GrantType.Implicit, GrantType.Hybrid, grantTypes); DisallowGrantTypeCombination(GrantType.AuthorizationCode, GrantType.Hybrid, grantTypes); } private static void DisallowGrantTypeCombination(string value1, string value2, IEnumerable grantTypes) { if (grantTypes.Contains(value1, StringComparer.Ordinal) && grantTypes.Contains(value2, StringComparer.Ordinal)) { throw new InvalidOperationException($"Grant types list cannot contain both {value1} and {value2}"); } } internal class GrantTypeValidatingHashSet : ICollection { private readonly ICollection _inner; public GrantTypeValidatingHashSet() { _inner = new HashSet(); } public GrantTypeValidatingHashSet(IEnumerable values) { _inner = new HashSet(values); } private ICollection Clone() { return new HashSet(this); } private ICollection CloneWith(params string[] values) { var clone = Clone(); foreach (var item in values) clone.Add(item); return clone; } public int Count => _inner.Count; public bool IsReadOnly => _inner.IsReadOnly; public void Add(string item) { ValidateGrantTypes(CloneWith(item)); _inner.Add(item); } public void Clear() { _inner.Clear(); } public bool Contains(string item) { return _inner.Contains(item); } public void CopyTo(string[] array, int arrayIndex) { _inner.CopyTo(array, arrayIndex); } public IEnumerator GetEnumerator() { return _inner.GetEnumerator(); } public bool Remove(string item) { return _inner.Remove(item); } IEnumerator IEnumerable.GetEnumerator() { return _inner.GetEnumerator(); } } } ================================================ FILE: src/Storage/src/Models/ClientClaim.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// A client claim /// public class ClientClaim { /// /// The claim type /// public string Type { get; set; } /// /// The claim value /// public string Value { get; set; } /// /// The claim value type /// public string ValueType { get; set; } = ClaimValueTypes.String; /// /// ctor /// public ClientClaim() { } /// /// ctor /// /// /// public ClientClaim(string type, string value) { Type = type; Value = value; } /// /// ctor /// /// /// /// public ClientClaim(string type, string value, string valueType) { Type = type; Value = value; ValueType = valueType; } /// public override int GetHashCode() { unchecked { int hash = 17; hash = hash * 23 + Value.GetHashCode(); hash = hash * 23 + Type.GetHashCode(); hash = hash * 23 + ValueType.GetHashCode(); return hash; } } /// public override bool Equals(object obj) { if (obj is null) return false; if (obj is ClientClaim c) { return (string.Equals(Type, c.Type, StringComparison.Ordinal) && string.Equals(Value, c.Value, StringComparison.Ordinal) && string.Equals(ValueType, c.ValueType, StringComparison.Ordinal)); } return false; } } ================================================ FILE: src/Storage/src/Models/Consent.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Represents the permissions (in terms of scopes) granted to a client by a subject /// public class Consent { /// /// Gets or sets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets or sets the scopes. /// /// /// The scopes. /// public IEnumerable Scopes { get; set; } /// /// Gets or sets the creation time. /// /// /// The creation time. /// public DateTime CreationTime { get; set; } /// /// Gets or sets the expiration. /// /// /// The expiration. /// public DateTime? Expiration { get; set; } } ================================================ FILE: src/Storage/src/Models/DeviceCode.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Represents data needed for device flow. /// public class DeviceCode { /// /// Gets or sets the creation time. /// /// /// The creation time. /// public DateTime CreationTime { get; set; } /// /// Gets or sets the lifetime. /// /// /// The lifetime. /// public int Lifetime { get; set; } /// /// Gets or sets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } /// /// Gets or sets a value indicating whether this instance is open identifier. /// /// /// true if this instance is open identifier; otherwise, false. /// public bool IsOpenId { get; set; } /// /// Gets or sets a value indicating whether this instance is authorized. /// /// /// true if this instance is authorized; otherwise, false. /// public bool IsAuthorized { get; set; } /// /// Gets or sets the requested scopes. /// /// /// The authorized scopes. /// public IEnumerable RequestedScopes { get; set; } /// /// Gets or sets the authorized scopes. /// /// /// The authorized scopes. /// public IEnumerable AuthorizedScopes { get; set; } /// /// Gets or sets the subject. /// /// /// The subject. /// public ClaimsPrincipal Subject { get; set; } /// /// Gets or sets the session identifier. /// /// /// The session identifier. /// public string SessionId { get; set; } } ================================================ FILE: src/Storage/src/Models/Enums.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// OpenID Connect subject types. /// public enum SubjectTypes { /// /// global - use the native subject id /// Global = 0, /// /// ppid - scope the subject id to the client /// Ppid = 1 } /// /// Access token types. /// public enum AccessTokenType { /// /// Self-contained Json Web Token /// Jwt = 0, /// /// Reference token /// Reference = 1 } /// /// Token usage types. /// public enum TokenUsage { /// /// Re-use the refresh token handle /// ReUse = 0, /// /// Issue a new refresh token handle every time /// OneTimeOnly = 1 } /// /// Token expiration types. /// public enum TokenExpiration { /// /// Sliding token expiration /// Sliding = 0, /// /// Absolute token expiration /// Absolute = 1 } /// /// Content Security Policy Level /// public enum CspLevel { /// /// Level 1 /// One = 0, /// /// Level 2 /// Two = 1 } ================================================ FILE: src/Storage/src/Models/GrantType.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Models; public static class GrantType { public const string Implicit = "implicit"; public const string Hybrid = "hybrid"; public const string AuthorizationCode = "authorization_code"; public const string ClientCredentials = "client_credentials"; public const string ResourceOwnerPassword = "password"; public const string DeviceFlow = "urn:ietf:params:oauth:grant-type:device_code"; } ================================================ FILE: src/Storage/src/Models/IdentityResource.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models a user identity resource. /// [DebuggerDisplay("{" + nameof(DebuggerDisplay) + ",nq}")] public class IdentityResource : Resource { private string DebuggerDisplay => Name ?? $"{{{typeof(IdentityResource)}}}"; /// /// Initializes a new instance of the class. /// public IdentityResource() { } /// /// Initializes a new instance of the class. /// /// The name. /// List of associated user claims that should be included when this resource is requested. public IdentityResource(string name, IEnumerable userClaims) : this(name, name, userClaims) { } /// /// Initializes a new instance of the class. /// /// The name. /// The display name. /// List of associated user claims that should be included when this resource is requested. /// name /// Must provide at least one claim type - claimTypes public IdentityResource(string name, string displayName, IEnumerable userClaims) { if (name.IsMissing()) throw new ArgumentNullException(nameof(name)); if (userClaims.IsNullOrEmpty()) throw new ArgumentException("Must provide at least one claim type", nameof(userClaims)); Name = name; DisplayName = displayName; foreach(var type in userClaims) { UserClaims.Add(type); } } /// /// Specifies whether the user can de-select the scope on the consent screen (if the consent screen wants to implement such a feature). Defaults to false. /// public bool Required { get; set; } = false; /// /// Specifies whether the consent screen will emphasize this scope (if the consent screen wants to implement such a feature). /// Use this setting for sensitive or important scopes. Defaults to false. /// public bool Emphasize { get; set; } = false; } ================================================ FILE: src/Storage/src/Models/PersistedGrant.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// A model for a persisted grant /// public class PersistedGrant { /// /// Gets or sets the key. /// /// /// The key. /// public string Key { get; set; } /// /// Gets the type. /// /// /// The type. /// public string Type { get; set; } /// /// Gets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId { get; set; } /// /// Gets the session identifier. /// /// /// The session identifier. /// public string SessionId { get; set; } /// /// Gets the client identifier. /// /// /// The client identifier. /// public string ClientId { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } /// /// Gets or sets the creation time. /// /// /// The creation time. /// public DateTime CreationTime { get; set; } /// /// Gets or sets the expiration. /// /// /// The expiration. /// public DateTime? Expiration { get; set; } /// /// Gets or sets the consumed time. /// /// /// The consumed time. /// public DateTime? ConsumedTime { get; set; } /// /// Gets or sets the data. /// /// /// The data. /// public string Data { get; set; } } ================================================ FILE: src/Storage/src/Models/RefreshToken.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models a refresh token. /// public class RefreshToken { /// /// Gets or sets the creation time. /// /// /// The creation time. /// public DateTime CreationTime { get; set; } /// /// Gets or sets the life time. /// /// /// The life time. /// public int Lifetime { get; set; } /// /// Gets or sets the consumed time. /// /// /// The consumed time. /// public DateTime? ConsumedTime { get; set; } /// /// Gets or sets the access token. /// /// /// The access token. /// public Token AccessToken { get; set; } /// /// Gets or sets the original subject that requiested the token. /// /// /// The subject. /// public ClaimsPrincipal Subject { get { var user = new IdentityServerUser(SubjectId); if (AccessToken.Claims != null) { foreach (var claim in AccessToken.Claims) { user.AdditionalClaims.Add(claim); } } return user.CreatePrincipal(); } } /// /// Gets or sets the version number. /// /// /// The version. /// public int Version { get; set; } = 4; /// /// Gets the client identifier. /// /// /// The client identifier. /// public string ClientId => AccessToken.ClientId; /// /// Gets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId => AccessToken.SubjectId; /// /// Gets the session identifier. /// /// /// The session identifier. /// public string SessionId => AccessToken.SessionId; /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description => AccessToken.Description; /// /// Gets the scopes. /// /// /// The scopes. /// public IEnumerable Scopes => AccessToken.Scopes; } ================================================ FILE: src/Storage/src/Models/Resource.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models the common data of API and identity resources. /// [DebuggerDisplay("{DebuggerDisplay,nq}")] public abstract class Resource { private string DebuggerDisplay => Name ?? $"{{{typeof(Resource)}}}"; /// /// Indicates if this resource is enabled. Defaults to true. /// public bool Enabled { get; set; } = true; /// /// The unique name of the resource. /// public string Name { get; set; } /// /// Display name of the resource. /// public string DisplayName { get; set; } /// /// Description of the resource. /// public string Description { get; set; } /// /// Specifies whether this scope is shown in the discovery document. Defaults to true. /// public bool ShowInDiscoveryDocument { get; set; } = true; /// /// List of associated user claims that should be included when this resource is requested. /// public ICollection UserClaims { get; set; } = new HashSet(); /// /// Gets or sets the custom properties for the resource. /// /// /// The properties. /// public IDictionary Properties { get; set; } = new Dictionary(); } ================================================ FILE: src/Storage/src/Models/Resources.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models a collection of identity and API resources. /// public class Resources { /// /// Initializes a new instance of the class. /// public Resources() { } /// /// Initializes a new instance of the class. /// /// The other. public Resources(Resources other) : this(other.IdentityResources, other.ApiResources, other.ApiScopes) { OfflineAccess = other.OfflineAccess; } /// /// Initializes a new instance of the class. /// /// The identity resources. /// The API resources. /// The API scopes. public Resources(IEnumerable identityResources, IEnumerable apiResources, IEnumerable apiScopes) { if (identityResources?.Any() == true) { IdentityResources = new HashSet(identityResources.ToArray()); } if (apiResources?.Any() == true) { ApiResources = new HashSet(apiResources.ToArray()); } if (apiScopes?.Any() == true) { ApiScopes = new HashSet(apiScopes.ToArray()); } } /// /// Gets or sets a value indicating whether [offline access]. /// /// /// true if [offline access]; otherwise, false. /// public bool OfflineAccess { get; set; } /// /// Gets or sets the identity resources. /// public ICollection IdentityResources { get; set; } = new HashSet(); /// /// Gets or sets the API resources. /// public ICollection ApiResources { get; set; } = new HashSet(); /// /// Gets or sets the API scopes. /// public ICollection ApiScopes { get; set; } = new HashSet(); } ================================================ FILE: src/Storage/src/Models/Secret.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models a client secret with identifier and expiration /// public class Secret { /// /// Gets or sets the description. /// /// /// The description. /// public string Description { get; set; } /// /// Gets or sets the value. /// /// /// The value. /// public string Value { get; set; } /// /// Gets or sets the expiration. /// /// /// The expiration. /// public DateTime? Expiration { get; set; } /// /// Gets or sets the type of the client secret. /// /// /// The type of the client secret. /// public string Type { get; set; } /// /// Initializes a new instance of the class. /// public Secret() { Type = IdentityServerConstants.SecretTypes.SharedSecret; } /// /// Initializes a new instance of the class. /// /// The value. /// The expiration. public Secret(string value, DateTime? expiration = null) : this() { Value = value; Expiration = expiration; } /// /// Initializes a new instance of the class. /// /// The value. /// The description. /// The expiration. public Secret(string value, string description, DateTime? expiration = null) : this() { Description = description; Value = value; Expiration = expiration; } /// /// Returns a hash code for this instance. /// /// /// A hash code for this instance, suitable for use in hashing algorithms and data structures like a hash table. /// public override int GetHashCode() { unchecked { var hash = 17; hash = hash * 23 + (Value?.GetHashCode() ?? 0); hash = hash * 23 + (Type?.GetHashCode() ?? 0); return hash; } } /// /// Determines whether the specified , is equal to this instance. /// /// The to compare with this instance. /// /// true if the specified is equal to this instance; otherwise, false. /// public override bool Equals(object obj) { if (obj == null) return false; var other = obj as Secret; if (other == null) return false; if (ReferenceEquals(other, this)) return true; return String.Equals(other.Type, Type, StringComparison.Ordinal) && String.Equals(other.Value, Value, StringComparison.Ordinal); } } ================================================ FILE: src/Storage/src/Models/Token.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Models; /// /// Models a token. /// public class Token { /// /// Initializes a new instance of the class. /// public Token() { } /// /// Initializes a new instance of the class. /// /// Type of the token. public Token(string tokenType) { Type = tokenType; } /// /// A list of allowed algorithm for signing the token. If null or empty, will use the default algorithm. /// public ICollection AllowedSigningAlgorithms { get; set; } = new HashSet(); /// /// Specifies the confirmation method of the token. This value, if set, will become the cnf claim. /// public string Confirmation { get; set; } /// /// Gets or sets the audiences. /// /// /// The audiences. /// public ICollection Audiences { get; set; } = new HashSet(); /// /// Gets or sets the issuer. /// /// /// The issuer. /// public string Issuer { get; set; } /// /// Gets or sets the creation time. /// /// /// The creation time. /// public DateTime CreationTime { get; set; } /// /// Gets or sets the lifetime. /// /// /// The lifetime. /// public int Lifetime { get; set; } /// /// Gets or sets the type. /// /// /// The type. /// public string Type { get; set; } = OidcConstants.TokenTypes.AccessToken; /// /// Gets or sets the ID of the client. /// /// /// The ID of the client. /// public string ClientId { get; set; } /// /// Gets or sets the type of access token of the client /// /// /// The access token type specified by the client. /// public AccessTokenType AccessTokenType { get; set; } /// /// Gets the description the user assigned to the device being authorized. /// /// /// The description. /// public string Description { get; set; } /// /// Gets or sets the claims. /// /// /// The claims. /// public ICollection Claims { get; set; } = new HashSet(new ClaimComparer()); /// /// Gets or sets the version. /// /// /// The version. /// public int Version { get; set; } = 4; /// /// Gets the subject identifier. /// /// /// The subject identifier. /// public string SubjectId => Claims.Where(x => x.Type == JwtClaimTypes.Subject).Select(x => x.Value).SingleOrDefault(); /// /// Gets the session identifier. /// /// /// The session identifier. /// public string SessionId => Claims.Where(x => x.Type == JwtClaimTypes.SessionId).Select(x => x.Value).SingleOrDefault(); /// /// Gets the scopes. /// /// /// The scopes. /// public IEnumerable Scopes => Claims.Where(x => x.Type == JwtClaimTypes.Scope).Select(x => x.Value); } ================================================ FILE: src/Storage/src/Services/ICorsPolicyService.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Services; /// /// Service that determines if CORS is allowed. /// public interface ICorsPolicyService { /// /// Determines whether origin is allowed. /// /// The origin. /// Task IsOriginAllowedAsync(string origin); } ================================================ FILE: src/Storage/src/Stores/IAuthorizationCodeStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for the authorization code store /// public interface IAuthorizationCodeStore { /// /// Stores the authorization code. /// /// The code. /// Task StoreAuthorizationCodeAsync(AuthorizationCode code); /// /// Gets the authorization code. /// /// The code. /// Task GetAuthorizationCodeAsync(string code); /// /// Removes the authorization code. /// /// The code. /// Task RemoveAuthorizationCodeAsync(string code); } ================================================ FILE: src/Storage/src/Stores/IClientStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Retrieval of client configuration /// public interface IClientStore { /// /// Finds a client by id /// /// The client id /// The client Task FindClientByIdAsync(string clientId); } ================================================ FILE: src/Storage/src/Stores/IDeviceFlowStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for the device flow store /// public interface IDeviceFlowStore { /// /// Stores the device authorization request. /// /// The device code. /// The user code. /// The data. /// Task StoreDeviceAuthorizationAsync(string deviceCode, string userCode, DeviceCode data); /// /// Finds device authorization by user code. /// /// The user code. /// Task FindByUserCodeAsync(string userCode); /// /// Finds device authorization by device code. /// /// The device code. Task FindByDeviceCodeAsync(string deviceCode); /// /// Updates device authorization, searching by user code. /// /// The user code. /// The data. Task UpdateByUserCodeAsync(string userCode, DeviceCode data); /// /// Removes the device authorization, searching by device code. /// /// The device code. Task RemoveByDeviceCodeAsync(string deviceCode); } ================================================ FILE: src/Storage/src/Stores/IPersistedGrantStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for persisting any type of grant. /// public interface IPersistedGrantStore { /// /// Stores the grant. /// /// The grant. /// Task StoreAsync(PersistedGrant grant); /// /// Gets the grant. /// /// The key. /// Task GetAsync(string key); /// /// Gets all grants based on the filter. /// /// The filter. /// Task> GetAllAsync(PersistedGrantFilter filter); /// /// Removes the grant by key. /// /// The key. /// Task RemoveAsync(string key); /// /// Removes all grants based on the filter. /// /// The filter. /// Task RemoveAllAsync(PersistedGrantFilter filter); } ================================================ FILE: src/Storage/src/Stores/IReferenceTokenStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for reference token storage /// public interface IReferenceTokenStore { /// /// Stores the reference token. /// /// The token. /// Task StoreReferenceTokenAsync(Token token); /// /// Gets the reference token. /// /// The handle. /// Task GetReferenceTokenAsync(string handle); /// /// Removes the reference token. /// /// The handle. /// Task RemoveReferenceTokenAsync(string handle); /// /// Removes the reference tokens. /// /// The subject identifier. /// The client identifier. /// Task RemoveReferenceTokensAsync(string subjectId, string clientId); } ================================================ FILE: src/Storage/src/Stores/IRefreshTokenStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for refresh token storage /// public interface IRefreshTokenStore { /// /// Stores the refresh token. /// /// The refresh token. /// Task StoreRefreshTokenAsync(RefreshToken refreshToken); /// /// Updates the refresh token. /// /// The handle. /// The refresh token. /// Task UpdateRefreshTokenAsync(string handle, RefreshToken refreshToken); /// /// Gets the refresh token. /// /// The refresh token handle. /// Task GetRefreshTokenAsync(string refreshTokenHandle); /// /// Removes the refresh token. /// /// The refresh token handle. /// Task RemoveRefreshTokenAsync(string refreshTokenHandle); /// /// Removes the refresh tokens. /// /// The subject identifier. /// The client identifier. /// Task RemoveRefreshTokensAsync(string subjectId, string clientId); } ================================================ FILE: src/Storage/src/Stores/IResourceStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Resource retrieval /// public interface IResourceStore { /// /// Gets identity resources by scope name. /// Task> FindIdentityResourcesByScopeNameAsync(IEnumerable scopeNames); /// /// Gets API scopes by scope name. /// Task> FindApiScopesByNameAsync(IEnumerable scopeNames); /// /// Gets API resources by scope name. /// Task> FindApiResourcesByScopeNameAsync(IEnumerable scopeNames); /// /// Gets API resources by API resource name. /// Task> FindApiResourcesByNameAsync(IEnumerable apiResourceNames); /// /// Gets all resources. /// Task GetAllResourcesAsync(); } ================================================ FILE: src/Storage/src/Stores/IUserConsentStore.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Interface for user consent storage /// public interface IUserConsentStore { /// /// Stores the user consent. /// /// The consent. /// Task StoreUserConsentAsync(Consent consent); /// /// Gets the user consent. /// /// The subject identifier. /// The client identifier. /// Task GetUserConsentAsync(string subjectId, string clientId); /// /// Removes the user consent. /// /// The subject identifier. /// The client identifier. /// Task RemoveUserConsentAsync(string subjectId, string clientId); } ================================================ FILE: src/Storage/src/Stores/PersistedGrantFilter.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores; /// /// Represents a filter used when accessing the persisted grants store. /// Setting multiple properties is interpreted as a logical 'AND' to further filter the query. /// At least one value must be supplied. /// public class PersistedGrantFilter { /// /// Subject id of the user. /// public string SubjectId { get; set; } /// /// Session id used for the grant. /// public string SessionId { get; set; } /// /// Client id the grant was issued to. /// public string ClientId { get; set; } /// /// The type of grant. /// public string Type { get; set; } } ================================================ FILE: src/Storage/src/Stores/Serialization/ClaimConverter.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Stores.Serialization; public class ClaimConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(Claim) == objectType; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var source = serializer.Deserialize(reader); var target = new Claim(source.Type, source.Value, source.ValueType); return target; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var source = (Claim)value; var target = new ClaimLite { Type = source.Type, Value = source.Value, ValueType = source.ValueType }; serializer.Serialize(writer, target); } } ================================================ FILE: src/Storage/src/Stores/Serialization/ClaimLite.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Stores.Serialization; public class ClaimLite { public string Type { get; set; } public string Value { get; set; } public string ValueType { get; set; } } ================================================ FILE: src/Storage/src/Stores/Serialization/ClaimsPrincipalConverter.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Stores.Serialization; public class ClaimsPrincipalConverter : JsonConverter { public override bool CanConvert(Type objectType) { return typeof(ClaimsPrincipal) == objectType; } public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) { var source = serializer.Deserialize(reader); if (source == null) return null; var claims = source.Claims.Select(x => new Claim(x.Type, x.Value, x.ValueType)); var id = new ClaimsIdentity(claims, source.AuthenticationType, JwtClaimTypes.Name, JwtClaimTypes.Role); var target = new ClaimsPrincipal(id); return target; } public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) { var source = (ClaimsPrincipal)value; var target = new ClaimsPrincipalLite { AuthenticationType = source.Identity.AuthenticationType, Claims = source.Claims.Select(x => new ClaimLite { Type = x.Type, Value = x.Value, ValueType = x.ValueType }).ToArray() }; serializer.Serialize(writer, target); } } ================================================ FILE: src/Storage/src/Stores/Serialization/ClaimsPrincipalLite.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Stores.Serialization; public class ClaimsPrincipalLite { public string AuthenticationType { get; set; } public ClaimLite[] Claims { get; set; } } ================================================ FILE: src/Storage/src/Stores/Serialization/CustomContractResolver.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ #pragma warning disable 1591 namespace IdentityServer8.Stores.Serialization; public class CustomContractResolver: DefaultContractResolver { protected override IList CreateProperties(Type type, MemberSerialization memberSerialization) { var props = base.CreateProperties(type, memberSerialization); return props.Where(p => p.Writable).ToList(); } } ================================================ FILE: src/Storage/src/Stores/Serialization/IPersistentGrantSerializer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores.Serialization; /// /// Interface for persisted grant serialization /// public interface IPersistentGrantSerializer { /// /// Serializes the specified value. /// /// /// The value. /// string Serialize(T value); /// /// Deserializes the specified string. /// /// /// The json. /// T Deserialize(string json); } ================================================ FILE: src/Storage/src/Stores/Serialization/PersistentGrantSerializer.cs ================================================ /* Copyright (c) 2024 HigginsSoft, Alexander Higgins - https://github.com/alexhiggins732/ Copyright (c) 2018, Brock Allen & Dominick Baier. All rights reserved. Licensed under the Apache License, Version 2.0. See LICENSE in the project root for license information. Source code and license this software can be found The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. */ namespace IdentityServer8.Stores.Serialization; /// /// JSON-based persisted grant serializer /// /// public class PersistentGrantSerializer : IPersistentGrantSerializer { private static readonly JsonSerializerSettings _settings; static PersistentGrantSerializer() { _settings = new JsonSerializerSettings { ContractResolver = new CustomContractResolver() }; _settings.Converters.Add(new ClaimConverter()); _settings.Converters.Add(new ClaimsPrincipalConverter()); } /// /// Serializes the specified value. /// /// /// The value. /// public string Serialize(T value) { return JsonConvert.SerializeObject(value, _settings); } /// /// Deserializes the specified string. /// /// /// The json. /// public T Deserialize(string json) { return JsonConvert.DeserializeObject(json, _settings); } } ================================================ FILE: version.json ================================================ { "$schema": "https://raw.githubusercontent.com/dotnet/Nerdbank.GitVersioning/main/src/NerdBank.GitVersioning/version.schema.json", "version": "8.0.4-beta.4", "nugetPackageVersion": { "semVer": 2, // optional. Set to either 1 or 2 to control how the NuGet package version string is generated. Default is 1. "precision": "revision" }, "semVer1NumericIdentifierPadding": 4, "pathFilters": [ // optional list of paths to consider when calculating version height. "!docs", "!.git", "!.vs", "!.config", "!.github", "!.config", "!Directory.Build.props", "!Directory.Build.targets", "!Directory.Packages.props", "docker-compose.yml", "docker-compose.override.yml", "docker-compose.vs.debug.yml", "docker-compose.vs.release.yml", "docker-compose.dcrpoj", "src/IdentityServer8.sln", "!global.json", "!IdentityServer8.DotNet.ruleset", "!LICENSCE", "!Nuget.config", "!SECURITY.md", "!SPONSORS.md", "!README.md", "!samples", "!nuget" ], "publicReleaseRefSpec": [ "^refs/heads/master$", // we release out of master "^refs/tags/v\\d+\\.\\d+", // we also release tags starting with vN.N "^refs/heads/develop$", // we release alpha out of develop "^refs/heads/release/*" // we release beta out of release ], "cloudBuild": { "setVersionVariables": true, "buildNumber": { "enabled": true, "includeCommitId": { "when": "always", "where": "buildMetadata" } } }, "release": { "tagName": "v{version}", "branchName": "v{version}", "versionIncrement": "minor", "firstUnstableTag": "alpha" } }